Skip to content

Diagnostics & troubleshooting

Background location is hard to debug because the interesting events happen when no one is watching — after the app is backgrounded, while it’s terminated, on someone else’s phone. This page covers the on-device log buffer, what Beekon throws vs. surfaces as a state, and a symptom → fix list.

Beekon keeps an on-device log buffer you can read, export, and tail, so you can see what the SDK did while you weren’t looking. Logging is observational and self-contained: it never sends anything off the device.

MethodWhat it does
setLogLevel(level)Set the verbosity threshold at runtime — entries below it aren’t recorded.
log(level, message)Write your own breadcrumb into the SDK’s pipeline (category app).
getLog(from, to)Read persisted entries in a time range, oldest first.
exportLog()Serialize the buffer to an NDJSON file; returns the file path (attach to a bug report).
clearLog()Delete all persisted entries.
logs (stream)Live tail — each entry as it’s recorded.

A LogEntry is { id, timestamp, level, category, message }category is the subsystem (location, sync, …), or app for breadcrumbs you write. Levels, most to least severe: error, warn, info, debug, verbose (plus off).

Beekon.setLogLevel(LogLevel.Debug)
Beekon.log(LogLevel.Info, "user tapped 'start trip'")
lifecycleScope.launch {
Beekon.logs.collect { e -> Log.d("beekon", "[${e.level}] ${e.category}: ${e.message}") }
}
val entries = Beekon.getLog(from = hourAgo, to = Instant.now())
val ndjsonPath = Beekon.exportLog()

Beekon errors are typed, but the SDK draws a hard line: start() never throws. Thrown errors are limited to storage failures (from history reads), geofence-validation failures (from addGeofences), location-unavailable failures (from getCurrentLocation on a precondition failure), and invalid-configuration failures (from configure). Everything else — permission, services, system kills — is a runtime condition that surfaces as Stopped(reason) on the state stream, not an exception.

The per-platform types:

  • Androidsealed class BeekonException with five subclasses: StorageException(cause), InvalidConfiguration(reason), InvalidGeofence(reason), GeofencesManagedByServer, LocationUnavailable(reason).
  • iOSenum BeekonError: Error with five cases: .invalidConfiguration(reason:), .invalidGeofence(reason:), .geofencesManagedByServer, .storage(reason:), .locationUnavailable(reason:).
ScenarioSurface
User has never granted permission, you call start()start() returns; state → Stopped(PermissionDenied) — no exception
User revokes permission while trackingstate → Stopped(PermissionDenied) — no exception
Android OS kills the foreground service under memory pressurestate → Stopped(System) — Android only
getLocations(...) hits a SQLite failurethrows BeekonException.StorageException / BeekonError.storage
addGeofences(...) given a bad regionthrows BeekonException.InvalidGeofence / BeekonError.invalidGeofence
configure(...) given an out-of-range value (e.g. stationary radius outside 5–1000 m)throws BeekonException.InvalidConfiguration / BeekonError.invalidConfiguration

There’s no NotInitialised or NotConfigured — calling start() without prior configure(...) simply uses the persisted last-known config (or the default on a fresh install). GeofencesManagedByServer fires only in Beekon Cloud mode.

Observe the state stream for everything to do with start/stop; reserve try/catch for history and geofence calls. Beekon does not auto-resume after a stop — the host app calls start() again once the underlying condition recovers.

Symptom → likely cause → fix. Most issues are permissions, the gate, or host wiring — not the SDK.

start() never throws — if it can’t run, the truth lands on state as Stopped(reason). Observe state and read the reason: permissionDenied, locationServicesDisabled, locationUnavailable, system. See Lifecycle & states.

  • The gate is too tight. A fix is admitted only when both intervalSeconds and distanceMeters pass. Lower them (or set to 0 to disable a gate). See Configuration.
  • No background permission. Foreground-only grants stop updates the moment the app backgrounds. You need Always / background location.
  • iOS authorization is two-step. iOS grants When in Use first; you must then request Always. See Platform setup.
  • Wrong status code. The SDK only deletes rows on 2xx. A 4xx you didn’t mean (e.g. a framework returning 404 for an unmatched route) keeps the batch and looks like a stuck queue. Confirm your route returns 2xx.
  • A redirect. Beekon refuses 3xx and treats it as permanent — serve the exact url directly, with no 301/302.
  • You’re not gunzipping. The body is always Content-Encoding: gzip. A server that hands raw bytes to a JSON parser will 400. Gunzip first.
  • Auth. A 401/403 surfaces as a sync auth failure. With SyncConfig.auth set, the SDK refreshes and retries; without it, re-seed by calling configure() again. See Auth & token refresh.
  • Watch syncStatus and pendingUploadCount() to see whether the queue is draining.
  • Radius too small. Use ≥ 100 m — smaller regions are unreliable on real hardware.
  • Background location not granted. Geofence monitoring needs the same Always/background grant as tracking.
  • Notifications (Android 13+). If you also surface a notification on a crossing, you need POST_NOTIFICATIONS.
  • Subscribe to geofenceEvents to confirm crossings are being recorded. See Geofencing.

Tracking doesn’t resume after the app is killed

Section titled “Tracking doesn’t resume after the app is killed”
  • Android — wire the cold-launch resume. Call Beekon.start() (or resumeIfNeeded() on the wrappers) from Application.onCreate.
  • iOS — it’s automatic, with a catch. A Significant Location Change wakes the terminated app and Beekon resumes. But if the user force-quits the app, iOS disables SLC wake until the app is next opened from the home screen — Apple’s rule, not Beekon’s.
  • Flutter / React Native expose resumeIfNeeded(); call it early in startup (and on Android, also from Application.onCreate).

Aggressive OEM battery managers (Xiaomi, Oppo, Huawei, …) kill background services regardless of the foreground-service contract. There’s no SDK fix — guide users to exempt your app. dontkillmyapp.com has per-vendor steps.

React Native: module is undefined / build fails

Section titled “React Native: module is undefined / build fails”

@wayq/beekon-rn requires the New Architecture (RN 0.76+). On the old architecture the TurboModule won’t link. Enable the New Architecture in your app.