---
title: Debugging Background Tasks
description: Debug and troubleshoot background tasks on Android and iOS
---

Background tasks can be tricky to debug since they run when your app is closed. Here's how to effectively debug and troubleshoot them on both platforms.

## Hook-Based Debug System

The Workmanager plugin uses a hook-based debug system that allows you to customize how debug information is handled.

### Quick Setup

Initialize Workmanager without any debug parameters:

```dart
await Workmanager().initialize(callbackDispatcher);
```

Then set up platform-specific debug handlers as needed.

## Android Debug Handlers

### Logging Debug Handler (Recommended)

Shows debug information in Android's Log system (visible in `adb logcat`):

```kotlin
// In your Application class
import dev.fluttercommunity.workmanager.WorkmanagerDebug
import dev.fluttercommunity.workmanager.LoggingDebugHandler

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        WorkmanagerDebug.setCurrent(LoggingDebugHandler())
    }
}
```

### Notification Debug Handler

Shows debug information as notifications (requires notification permissions):

```kotlin
import dev.fluttercommunity.workmanager.NotificationDebugHandler

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        WorkmanagerDebug.setCurrent(NotificationDebugHandler())
    }
}
```

## iOS Debug Handlers

### Logging Debug Handler (Recommended)

Shows debug information in iOS's unified logging system:

```swift
// In your AppDelegate.swift
import workmanager_apple

@main
class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        WorkmanagerDebug.setCurrent(LoggingDebugHandler())
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
```

### Notification Debug Handler

Shows debug information as notifications:

```swift
WorkmanagerDebug.setCurrent(NotificationDebugHandler())
```

## Custom Debug Handlers

Create your own debug handler for custom logging needs:

<Tabs>
  <TabItem label="Android" value="android">

```kotlin
class CustomDebugHandler : WorkmanagerDebug() {
    override fun onTaskStatusUpdate(context: Context, taskInfo: TaskDebugInfo, status: TaskStatus, result: TaskResult?) {
        // Custom status handling logic
        // See Task Status documentation for detailed status information
    }
    
    override fun onExceptionEncountered(context: Context, taskInfo: TaskDebugInfo?, exception: Throwable) {
        // Handle exceptions
    }
}

WorkmanagerDebug.setCurrent(CustomDebugHandler())
```

  </TabItem>
  <TabItem label="iOS" value="ios">

```swift
class CustomDebugHandler: WorkmanagerDebug {
    override func onTaskStatusUpdate(taskInfo: TaskDebugInfo, status: TaskStatus, result: TaskResult?) {
        // Custom status handling logic  
        // See Task Status documentation for detailed status information
    }
    
    override func onExceptionEncountered(taskInfo: TaskDebugInfo?, exception: Error) {
        // Handle exceptions
    }
}

WorkmanagerDebug.setCurrent(CustomDebugHandler())
```

  </TabItem>
</Tabs>

For detailed information about task statuses, lifecycle, and notification formats, see the [Task Status Tracking](task-status) guide.

## Android Debugging

### Job Scheduler Inspection

Use ADB to inspect Android's job scheduler:

```bash
# View scheduled jobs
adb shell dumpsys jobscheduler | grep yourapp

# View detailed job info  
adb shell dumpsys jobscheduler yourapp

# Force run job (debug only)
adb shell cmd jobscheduler run -f yourapp JOB_ID
```

### Monitor Job Execution

```bash
# Monitor WorkManager logs
adb logcat | grep WorkManager

# Monitor app background execution
adb logcat | grep "yourapp"
```

### Common Android Issues

**Tasks not running:**
- Check battery optimization settings
- Verify app is not in "App Standby" mode
- Ensure device isn't in Doze mode
- Check if constraints are too restrictive

**Tasks running too often:**
- Android enforces minimum 15-minute intervals for periodic tasks
- Use appropriate constraints to limit execution

### Debug Commands

```bash
# Check if your app is whitelisted from battery optimization
adb shell dumpsys deviceidle whitelist

# Check battery optimization status
adb shell settings get global battery_saver_constants

# Force device into idle mode (testing)
adb shell dumpsys deviceidle force-idle
```

## iOS Debugging

### Console Logging

iOS background tasks have limited execution time. Add detailed logging:

```dart
@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    print('[iOS BG] Task started: $task at ${DateTime.now()}');
    
    try {
      // Your task logic
      final result = await performTask();
      print('[iOS BG] Task completed successfully');
      return true;
    } catch (e) {
      print('[iOS BG] Task failed: $e');
      return false;
    }
  });
}
```

### Xcode Debugging

**For Background Fetch tasks:**
Use **Debug → Perform Fetch** from the Xcode run menu while your app is running.

**For BGTaskScheduler tasks (processing/periodic):**
Use Xcode's console to trigger tasks manually:

```objc
// Trigger specific BGTaskScheduler task
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] 
  _simulateLaunchForTaskWithIdentifier:@"com.yourapp.task.identifier"]
```

### Monitor Scheduled Tasks

Check what tasks iOS has scheduled:

```dart
// iOS 13+ only
if (Platform.isIOS) {
  final tasks = await Workmanager().printScheduledTasks();
  print('Scheduled tasks: $tasks');
}
```

This prints output like:
```
[BGTaskScheduler] Task Identifier: your.task.id earliestBeginDate: 2023.10.10 PM 11:10:12
[BGTaskScheduler] There are no scheduled tasks
```

### Common iOS Issues

**Background App Refresh disabled:**
- Check iOS Settings → General → Background App Refresh
- See [Apple's Background App Refresh Guide](https://support.apple.com/en-us/102425)

**Tasks never run:**
- App hasn't been used recently (iOS learning algorithm) - [iOS Power Management](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/PrioritizeWorkAtTheTaskLevel.html)
- Task identifiers don't match between Info.plist and AppDelegate - [check iOS setup](quickstart#option-b-processing-tasks-for-complex-operations)
- Missing BGTaskSchedulerPermittedIdentifiers in Info.plist - [review iOS configuration](quickstart#ios)

**Tasks stop working:**
- iOS battery optimization kicked in - [WWDC 2020: Background execution demystified](https://developer.apple.com/videos/play/wwdc2020/10063/)
- App removed from recent apps too often
- User disabled background refresh - [check Background Fetch setup](quickstart#option-a-periodic-tasks-recommended-for-most-use-cases)
- Task taking longer than 30 seconds - [BGTaskScheduler Documentation](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler)

**Tasks run but don't complete:**
- Hitting 30-second execution limit - [Background Tasks Best Practices](https://developer.apple.com/documentation/backgroundtasks/bgtask)
- Network requests timing out
- Heavy processing blocking the thread - [WWDC 2019: Advances in App Background Execution](https://developer.apple.com/videos/play/wwdc2019/707/)

## General Debugging Tips

### Add Comprehensive Logging

```dart
@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    final startTime = DateTime.now();
    print('🚀 Task started: $task');
    print('📊 Input data: $inputData');
    
    try {
      // Your task implementation
      final result = await performTask(task, inputData);
      
      final duration = DateTime.now().difference(startTime);
      print('✅ Task completed in ${duration.inSeconds}s');
      
      return result;
    } catch (e, stackTrace) {
      final duration = DateTime.now().difference(startTime);
      print('❌ Task failed after ${duration.inSeconds}s: $e');
      print('📋 Stack trace: $stackTrace');
      
      return false; // Retry
    }
  });
}
```

### Test Task Logic Separately

Create a way to test your background logic from the UI:

```dart
// Add a debug button to test task logic
ElevatedButton(
  onPressed: () async {
    // Test the same logic that runs in background
    final result = await performTask('test_task', {'debug': true});
    print('Test result: $result');
  },
  child: Text('Test Task Logic'),
)
```

### Monitor Task Health

Track when tasks last ran successfully:

```dart
Future<void> saveTaskExecutionTime(String taskName) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('${taskName}_last_run', DateTime.now().toIso8601String());
}

Future<bool> isTaskHealthy(String taskName, Duration maxAge) async {
  final prefs = await SharedPreferences.getInstance();
  final lastRunString = prefs.getString('${taskName}_last_run');
  
  if (lastRunString == null) return false;
  
  final lastRun = DateTime.parse(lastRunString);
  final age = DateTime.now().difference(lastRun);
  
  return age < maxAge;
}
```

## Platform-Specific Testing

### Android Testing Workflow

1. **Enable debug mode** and install app
2. **Schedule task** and verify it appears in job scheduler
3. **Put device to sleep** and wait for execution
4. **Check debug notifications** to confirm execution
5. **Use ADB commands** to force execution if needed

### iOS Testing Workflow  

1. **Test on physical device** (simulator doesn't support background tasks)
2. **Enable Background App Refresh** in iOS Settings
3. **Use Xcode debugger** to trigger tasks immediately
4. **Monitor Xcode console** for logging output
5. **Check iOS Settings > Battery** for background activity

## Troubleshooting Checklist

**Quick Checks:**
- [ ] Workmanager initialized in main()
- [ ] Task names are unique
- [ ] Platform setup completed ([iOS setup guide](quickstart#ios))
- [ ] Debug handler configured (see [Debug Handlers](#debug-handlers))

**Performance & Reliability:**
- [ ] Task logic optimized for background execution
- [ ] Dependencies initialized in background isolate
- [ ] Error handling with try-catch blocks
- [ ] iOS 30-second execution limit respected

Remember: Background task execution is controlled by the operating system and is never guaranteed. Always design your app to work gracefully when background tasks don't run as expected.