transistorsoft / transistorsoft/react-native-background-geolocation
[Bug]: uploadLog on iOS does not attach http.headers/authorization, Android does.
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.9k
- Forks
- 446
- PR merge metrics
- No merged PRs in 30d
Description
Required Reading
- Confirmed
Plugin Version
5.6.0
Mobile operating-system(s)
- iOS
- Android
Device Manufacturer(s) and Model(s)
iphone 13
Device operating-systems(s)
ios 26
React Native / Expo version
0.86.3
What happened?
BackgroundGeolocation.logger.uploadLog(url, query) on iOS posts the gzipped log without any of the configured headers. The docs for uploadLog state: "The file-upload request will attach your configured Config.headers for authentication." On Android this is true; on iOS the request arrives at our server with no Authorization header at all.
Config applied right before the call (awaited):
await BackgroundGeolocation.setConfig({
http: {
headers: {
Authorization: 'Bearer <token>',
Accept: 'application/json',
},
},
authorization: {
strategy: 'JWT',
accessToken: '<token>',
},
});
await BackgroundGeolocation.logger.uploadLog(url, query);
No http.url is configured (we do not sync locations through the SDK); the SDK's only outbound request is this upload. Both http.headers and authorization were set, neither is applied.
Server-side evidence (same recording, same second)
SDK upload from iOS — no auth header, 401:
status: 401, has_authorization_header: false, content_length: 7914,
content_type: "multipart/form-data; boundary=background-geolocation-multipart-boundary",
user_agent: "WandelWeb/425 CFNetwork/3860.700.2 Darwin/25.6.0"
Our own fetch() fallback from the same app instance and token — auth header present, 201:
status: 201, has_authorization_header: true, content_length: 22827,
user_agent: "WandelWeb/425 CFNetwork/3860.700.2 Darwin/25.6.0"
Android SDK upload with the identical JS code — auth header present, 201:
status: 201, has_authorization_header: true,
content_type: "multipart/form-data; boundary=<uuid>", user_agent: "okhttp/4.12.0"
Aggravating factor When our server (Laravel) answered the unauthenticated request with a 302 to the login page, CFNetwork followed the redirect, received a 200 HTML page, and uploadLog resolved successfully. We then called destroyLog(), so the log was lost. uploadLog should treat a non-2xx (or a redirect to a non-2xx / non-JSON page) as failure; we have since made our server return 401 so the promise rejects.
Expected:
uploadLog on iOS attaches http.headers and the authorization header, the same as the location sync path and the same as the Android implementation.
Actual
Request is sent with the SDK's multipart body and state/model/platform fields, but without any configured header. Only works if the endpoint is unauthenticated.
Workaround
Catch the rejection and fall back to logger.getLog(query) + own fetch() with headers. This costs the gzip compression and holds the full log string in JS memory.
Plugin Code and/or Config
Applied once at startup with ready() (i18n strings shortened):
BackgroundGeolocation.ready({
maxDaysToPersist: 14,
maxRecordsToPersist: -1,
geolocation: {
desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.Navigation,
stopTimeout: 1440,
distanceFilter: 5,
fastestLocationUpdateInterval: 3000,
locationUpdateInterval: 5000,
disableElasticity: true,
elasticityMultiplier: 2,
stationaryRadius: 25,
activityType: 3, // CLActivityTypeFitness
filter: {
useKalman: true,
trackingAccuracyThreshold: 100,
},
locationAuthorizationRequest: 'Always',
locationAuthorizationAlert: { titleWhenInUse: '…', titleWhenOff: '…', instructions: '…', cancelButton: '…', settingsButton: '…' },
showsBackgroundLocationIndicator: true, // iOS only
},
app: {
preventSuspend: false,
enableHeadless: false,
stopOnTerminate: false, // true while no recording is active, see setConfig below
startOnBoot: false,
notification: {
title: 'WandelWeb',
text: '…',
priority: BackgroundGeolocation.NOTIFICATION_PRIORITY_MAX,
channelName: 'WandelWeb GPS Tracking',
sticky: true,
ongoing: true,
smallIcon: 'drawable/ic_stat_wandelweb',
largeIcon: 'mipmap/ic_launcher_round',
color: '#F65053',
},
backgroundPermissionRationale: { title: '…', message: '…', positiveAction: '…', negativeAction: '…' },
},
activity: {
triggerActivities: 'on_foot, walking, running',
disableStopDetection: false,
stopOnStationary: false,
},
http: {
// No url: the SDK never syncs locations. uploadLog() is the only HTTP call it makes for us.
timeout: 60000,
},
logger: {
logLevel: BackgroundGeolocation.LOG_LEVEL_INFO, // VERBOSE for opted-in users, OFF when not recording
logMaxDays: 2,
debug: false,
},
foregroundService: true,
saveBatteryOnBackground: false,
});
When the recording stops (before the upload):
await BackgroundGeolocation.setConfig({
app: { stopOnTerminate: true },
logger: { logLevel: BackgroundGeolocation.LOG_LEVEL_OFF },
});
Immediately before the upload (awaited):
await BackgroundGeolocation.setConfig({
http: {
headers: {
Authorization: 'Bearer <sanctum token>',
Accept: 'application/json',
},
},
authorization: {
strategy: 'JWT',
accessToken: '<sanctum token>',
},
});
await BackgroundGeolocation.logger.uploadLog(
'https://<host>/api/v5/recordings/17265/native-log?client_id=…&started_at=2026-09-13T19:04:45.427Z&ended_at=2026-09-13T19:05:13.065Z&app_version=4.32.0',
{ start: <startedAt - 5000>, end: <endedAt + 5000>, order: Logger.ORDER_ASC },
);
Note: the has_authorization_header: false result was already reproduced with only http.headers set (build without the authorization block); adding authorization did not change the outcome.
Relevant log output
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the iOS logger.uploadLog entry point and compare its request construction with the Android implementation and the documented Config.headers behavior. Reproduce the upload with the supplied configuration, then verify that configured headers are sent and that an unsuccessful upload is reported as failure rather than resolving after a redirect.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, ios, react-native, typescript
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100