transistorsoft / react-native-background-geolocation

Sophisticated, battery-conscious background-geolocation with motion-detection
http://shop.transistorsoft.com/pages/react-native-background-geolocation
MIT License
2.66k stars 426 forks source link

iOS devices throwing INVALID_TASK_ID exception #2194

Closed mcottingham closed 2 weeks ago

mcottingham commented 3 weeks ago

Your Environment


### Android headless task registration
```javascript
/* _layout.js */

BackgroundGeolocation.registerHeadlessTask(async (event) => {
    if (event.name === 'heartbeat') {
        await onLocation();
    }
});

onLocation handler


/* _layout.js */

const onLocation = async (l) => {
    let taskId = await BackgroundGeolocation.startBackgroundTask();
    try {
        let location = l;
        const { attributes: { email: userEmail } } = await Auth.currentAuthenticatedUser();
        if (!location) {
            location = await BackgroundGeolocation.getCurrentPosition({ desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_NAVIGATION, samples: 3 })
        }
        await store.dispatch(updateStatus({ email: userEmail, location: toAWSLocation(location.coords), timestamp: location.timestamp }))
    } catch (ex) {
        console.log("Failed to process location", ex);
    } finally {
        await BackgroundGeolocation.stopBackgroundTask(taskId);
    }
}

We are using expo-router. We have registered / setup the BackgroundGeolocation library inside our main _layout.js file.

Expected Behavior

Actual Behavior

Some example breadcrumb trails.. image image

Steps to Reproduce

  1. Looks like this issue is triggered when a user enters a low signal area.
christocracy commented 3 weeks ago

What makes you think the error is coming from background-geolocation?

mcottingham commented 3 weeks ago

We weren't seeing this error before we added background-geolocation. Also, seems to correspond to when the app stops sending location updates.

christocracy commented 3 weeks ago

Do you have a stacktrace?

mcottingham commented 3 weeks ago

image

Error: INVALID_TASK_ID: 0 at onUnhandled(/node_modules/promise/setimmediate/rejection-tracking.js:71:28) at apply(native) at setTimeout(/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:213:23) at _callTimer(/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:111:15) at callTimers(/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:359:17) at apply(native) at __callFunction(/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:433:34) at guard$argument_0(/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:113:26) at guard(/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:368:11) at callFunctionReturnFlushedQueue(/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:112:17)

Unfortunately it's not super helpful

christocracy commented 3 weeks ago

at setTimeout(/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:213:23)

What this about?

mcottingham commented 3 weeks ago

Not really sure. If I recall correctly these kinds of logs appear when the app is waiting for a Promise to resolve that isn't actually a promise.

christocracy commented 3 weeks ago

I see in the Javascript API the text INVALID_TASK_ID. This is a "soft" Promise rejection if the taskId provided to .stopBackgroundTask evaluates as undefined, null or 0.

I suggest you check the value of taskId before calling .stopBackgroundTask(taskId) or catch errors on it.

if (taskId > 0) {
  BackgroundGeolocation.stopBackgroundTask(taskId);
}

I've never heard of .startBackgroundTask not returning an integer > 0 but maybe you've used up all your background-time and the OS returns taskId: 0.

christocracy commented 3 weeks ago

The Javascript code doesn't actually need that if (!taskId) { ... } check since the native Obj-c code doesn't complain if you try to stop an iOS background-task with taskId: 0.

mcottingham commented 3 weeks ago

Ah interesting. I guess this begs the question as to why I'm getting 0 as a taskId back from .startBackgroundTask

My onLocation handler is quite simple, as you can see. It grabs the current authenticated user. It checks, did we get a location in the call to onLocation if so, call updateStatus which essentially just does an API call to our backend. If we didn't receive a location (ie. heartbeat event) then we request the latest location and then updateStatus.

Any idea how many background tasks that are allowed to coexist? I believe that the issue is triggered when users enter low signal areas, so it's possible that we have multiple background tasks alive at the same time until the API times out.

christocracy commented 3 weeks ago

See iOS API docs beginBackgroundTaskWithExpirationHandler:

Any idea how many background tasks that are allowed to coexist?

There is no limit. These are not strictly a "task running in a thread". They are more like a post-it note posted on the operating-system's "fridge". They are just a note to the OS for when it comes time to put your app to sleep -- the existence of an outstanding taskId will tell the OS to pause for a finite length of time, allowing one to finish doing their work.

Screenshot 2024-10-28 at 4 20 38 PM

If you receive a taskId == 0, don't do any work and don't call .stopBackgroundTask.

If the OS returns taskId == 0, you have not been granted any background-time.

mcottingham commented 2 weeks ago

Thank you, I added that check and I see that devices are no longer throwing that exception.