[notifications] fix background tasks not executing#43245
Merged
Conversation
Collaborator
|
The Pull Request introduced fingerprint changes against the base commit: fe7ef63 Fingerprint diff[
{
"op": "changed",
"beforeSource": {
"type": "dir",
"filePath": "../../packages/expo-notifications/android",
"reasons": [
"expoAutolinkingAndroid"
],
"hash": "329be3225a338cf67cdd5a9d92af0c4b6f7991ed"
},
"afterSource": {
"type": "dir",
"filePath": "../../packages/expo-notifications/android",
"reasons": [
"expoAutolinkingAndroid"
],
"hash": "81000096661a6ce74df807d2a21d4677c5f64996"
}
},
{
"op": "changed",
"beforeSource": {
"type": "dir",
"filePath": "../../packages/expo-task-manager/android",
"reasons": [
"expoAutolinkingAndroid"
],
"hash": "53386b5446326692d70aa9ff11e321f2529a129f"
},
"afterSource": {
"type": "dir",
"filePath": "../../packages/expo-task-manager/android",
"reasons": [
"expoAutolinkingAndroid"
],
"hash": "7ade474389f72c686df3908b8689efe52a007e9c"
}
}
]Generated by PR labeler 🤖 |
15453e8 to
ebe48df
Compare
Collaborator
|
Hi there! 👋 I'm a bot whose goal is to ensure your contributions meet our guidelines. I've found some issues in your pull request that should be addressed (click on them for more details) 👇
|
Contributor
|
Subscribed to pull request
Generated by CodeMention |
douglowder
approved these changes
Feb 18, 2026
benjaminkomen
pushed a commit
to benjaminkomen/expo
that referenced
this pull request
Feb 25, 2026
# Why closes expo#38223 The above issue didn't include full repro steps so here they are: ### Prerequisites - An Expo app with `expo-notifications` and `expo-task-manager` configured - A registered `BACKGROUND_NOTIFICATION_TASK` via `TaskManager.defineTask` and `Notifications.registerTaskAsync` ### Sending push notifications Install `expo-server-sdk`: ```bash mkdir /tmp/expo-push-test && cd /tmp/expo-push-test npm init -y && npm install expo-server-sdk ``` Create `send.mjs`: ```js import { Expo } from "expo-server-sdk"; const expo = new Expo(); const token = "ExponentPushToken[YOUR_TOKEN_HERE]"; const mode = process.argv[2] // data (headless) or regular notification const message = mode === "data" ? { to: token, data: { type: "background_task", timestamp: String(Date.now()) }, _contentAvailable: true } : { to: token, title: "Test", body: `Notification at ${new Date().toLocaleTimeString()}` }; const [chunk] = expo.chunkPushNotifications([message]); console.log(`Sending ${mode} notification...`); const result = await expo.sendPushNotificationsAsync(chunk); console.log("Result:", JSON.stringify(result, null, 2)); ``` ### Reproduction steps 1. **Launch the app** and confirm it's in the foreground 2. **Send a display notification:** 3. **Kill the app** (swipe away from recents) 4. **Tap the notification** from the system tray — the app opens 5. **Wait for the app to fully load** 6. **Send a data-only notification:** ### Expected behavior The `BACKGROUND_NOTIFICATION_TASK` executes and JS receives the notification. ### Actual behavior (before fix) The background task silently fails. All subsequent data-only notifications also fail — the task never runs again until the app is force-stopped and restarted. ### Root cause When tapping the notification in step 4, `handleNotificationResponse` called `runTaskManagerTasks` while the app was still launching. This started a headless React instance that raced with the foreground app. The foreground `TaskManager` got misclassified as headless (via `isStartedByHeadlessLoader`), then wiped by `invalidateAppRecord` — permanently breaking background task execution for the session. # How Only call `runTaskManagerTasks` for non-default notification actions (custom action buttons like "Mark as read"). The default tap (`DEFAULT_ACTION_IDENTIFIER`) always opens the app, so there's no need for headless task execution — the foreground app handles the response via listeners. The fix is in `if (!isAppInForeground() && notificationResponse.actionIdentifier != NotificationResponse.DEFAULT_ACTION_IDENTIFIER)` but I also resolved one older TODO around an awkward use of `WeakHashMap`. # Test Plan - tested on device using the repro steps # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why
closes #38223
The above issue didn't include full repro steps so here they are:
Prerequisites
expo-notificationsandexpo-task-managerconfiguredBACKGROUND_NOTIFICATION_TASKviaTaskManager.defineTaskandNotifications.registerTaskAsyncSending push notifications
Install
expo-server-sdk:Create
send.mjs:Reproduction steps
Expected behavior
The
BACKGROUND_NOTIFICATION_TASKexecutes and JS receives the notification.Actual behavior (before fix)
The background task silently fails. All subsequent data-only notifications also fail — the task never runs again until the app is force-stopped and restarted.
Root cause
When tapping the notification in step 4,
handleNotificationResponsecalledrunTaskManagerTaskswhile the app was still launching. This started a headless React instance that raced with the foreground app. The foregroundTaskManagergot misclassified as headless (viaisStartedByHeadlessLoader), then wiped byinvalidateAppRecord— permanently breaking background task execution for the session.How
Only call
runTaskManagerTasksfor non-default notification actions (custom action buttons like "Mark as read").The default tap (
DEFAULT_ACTION_IDENTIFIER) always opens the app, so there's no need for headless task execution — the foreground app handles the response via listeners.The fix is in
if (!isAppInForeground() && notificationResponse.actionIdentifier != NotificationResponse.DEFAULT_ACTION_IDENTIFIER)but I also resolved one older TODO around an awkward use ofWeakHashMap.Test Plan
Checklist
changelog.mdentry and rebuilt the package sources according to this short guidenpx expo prebuild& EAS Build (eg: updated a module plugin).