expo/expo · error · ForegroundServiceStartNotAllowedException
Couldn't start the foreground service. Foreground service ca
Error message
Couldn't start the foreground service. Foreground service cannot be started when the application is in the background
What it means
expo-location throws this when you ask it to start a location task configured with a `foregroundService` option while the Android app process is not in the foreground. Android 12+ (API 31) forbids apps from starting foreground services from the background, so the module checks AppForegroundedSingleton before registering the task and fails fast with ForegroundServiceStartNotAllowedException instead of letting the OS kill the start attempt.
Source
Thrown at packages/expo-location/android/src/main/java/expo/modules/location/LocationModule.kt:331
AsyncFunction<Boolean>("hasServicesEnabledAsync") {
return@AsyncFunction LocationHelpers.isAnyProviderAvailable(mContext)
}
AsyncFunction("startLocationUpdatesAsync") { taskName: String, options: LocationTaskOptions ->
val shouldUseForegroundService = options.foregroundService != null
if (isMissingForegroundPermissions()) {
throw LocationBackgroundUnauthorizedException()
}
// There are two ways of starting this service.
// 1. As a background location service, this requires the background location permission.
// 2. As a user-initiated foreground service with notification, this does NOT require the background location permission.
if (!shouldUseForegroundService && isMissingBackgroundPermissions()) {
throw LocationBackgroundUnauthorizedException()
}
if (!AppForegroundedSingleton.isForegrounded && options.foregroundService != null) {
throw ForegroundServiceStartNotAllowedException()
}
if (shouldUseForegroundService && !hasForegroundServicePermissions()) {
throw ForegroundServicePermissionsException()
}
mTaskManager.registerTask(taskName, LocationTaskConsumer::class.java, options.toMutableMap())
return@AsyncFunction
}
AsyncFunction("stopLocationUpdatesAsync") { taskName: String ->
mTaskManager.unregisterTask(taskName, LocationTaskConsumer::class.java)
return@AsyncFunction
}
AsyncFunction("hasStartedLocationUpdatesAsync") { taskName: String ->
return@AsyncFunction mTaskManager.taskHasConsumerOfClass(taskName, LocationTaskConsumer::class.java)
}View on GitHub (pinned to 7da61120be)
Solutions
- Ensure the call runs only while the app is foregrounded: check AppState/AppLifecycle state before calling and defer the start until the activity resumes.
- Remove the `foregroundService` option (use plain background location with BACKGROUND_LOCATION permission) if updates must start from the background.
- Start the service from a user-visible interaction (notification button, activity) rather than a background callback.
- On Android 12+, if a legitimate background start is needed, have the user grant exact-alarm/exemption paths or use a foregroundServiceType-compatible restart via the system (e.g. from an existing foreground service context).
Example fix
// before
await Location.startLocationUpdatesAsync(TASK, {
foregroundService: { notificationTitle: 'Tracking' },
});
// after
if (AppState.currentState !== 'active') {
// defer until foregrounded, or drop foregroundService
return;
}
await Location.startLocationUpdatesAsync(TASK, {
foregroundService: { notificationTitle: 'Tracking' },
}); Defensive patterns
Strategy: try-catch
Validate before calling
import { AppState } from 'react-native';
function canStartForegroundLocation() {
return AppState.currentState === 'active';
} Try / catch
try {
await Location.startLocationUpdatesAsync(TASK, { foregroundService: opts });
} catch (e) {
if (String(e.message).includes('Foreground service cannot be started when the application is in the background')) {
// defer until app returns to foreground
AppState.addEventListener('change', (s) => { if (s === 'active') restart(); });
} else throw e;
} Prevention
- Only call startLocationUpdatesAsync with foregroundService from user-visible, foregrounded code paths.
- Guard background-triggered starts with an AppState/lifecycle check.
- Prefer BACKGROUND_LOCATION permission for background-only tracking instead of foregroundService.
When it happens
Trigger: Calling Location.startLocationUpdatesAsync / startLocationTaskAsync with `foregroundService: {...}` set while ActivityManager.getRunningAppProcesses reports the app is not foregrounded — e.g. invoked from a headless task manager callback, background fetch, or a notification tap handler that runs before the activity resumes.
Common situations: Restarting continuous location tracking from a background task after the OS killed the app; scheduling location updates from a push handler; a JS timer or task-manager callback firing after the user backgrounds the app; testing on Android 12+ emulators where the activity is not focused.
Related errors
- Couldn't start the foreground service. Foreground service pe
- Could not find device with name: ${name}
- Failed to get properties for device (${device.pid}): ${error
- No Android connected device found, and no emulators could be
- Failed to take Android screenshot: ${error instanceof Error
AI-assisted analysis of expo/expo@7da61120be (2026-09-09).
Data as JSON: /api/errors/bb70f949483615bc.
Report an issue: GitHub.