transistorsoft / transistorsoft/react-native-background-geolocation

[Bug]: uploadLog on iOS does not attach http.headers/authorization, Android does.

Open
#2,660 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.