- Send config to native before starting tracking (SharedPreferences sync) - Register ConnectivityReceiver for network state detection - Fix Settings number fields using TextEditingControllers - Cancel stream subscription on widget dispose (memory leak) - Replace WorkManager with AlarmManager for heartbeat - Add log cleanup for logs older than 24 hours - Change HTTP method from GET to POST These fixes resolve the 'device always offline' issue where: 1. Config was not being sent to native service 2. ConnectivityReceiver was never registered 3. Settings number fields were not saving 4. Heartbeat never fired due to WorkManager process isolation 5. Server expected POST not GET
6.2 KiB
Traccar Client Bug Report: Location Data Not Reaching Server
Date: April 30, 2026 Status: Investigating
Issue Summary
The Traccar Client app shows the device as always offline and no location reports reach the self-hosted Traccar server, even when:
- The correct Device ID is set and registered on the server
- The correct Server URL is configured
- The tracking toggle is ON
- The "Send Location" button appears to work
Root Causes Identified
Critical Bugs
1. Heartbeat Never Fires (Process Isolation Issue)
File: lib/bridge/location_bridge.dart (Dart) and android/app/src/main/kotlin/com/traccar/traccar_client/location/HeartbeatScheduler.kt
Problem: The HeartbeatWorker runs in a separate process via Android WorkManager. When it executes, the static onHeartbeatCallback variable is always null because companion object static variables are not shared across process boundaries.
// HeartbeatScheduler.kt
class HeartbeatWorker(...) : Worker(...) {
override fun doWork(): Result {
onHeartbeatCallback?.invoke() // Always null!
return Result.success()
}
}
companion object {
var onHeartbeatCallback: (() -> Unit)? = null // Not shared across processes
}
Impact: Heartbeat locations are never sent, causing the device to appear offline after initial connection wears off.
2. ConnectivityReceiver Never Registered
File: android/app/src/main/kotlin/com/traccar/traccar_client/service/LocationTrackingService.kt
Problem: The ConnectivityReceiver is created in onCreate() but never registered with the system. The code creates the receiver but doesn't call registerReceiver().
// LocationTrackingService.kt - onCreate()
connectivityReceiver = ConnectivityReceiver { online ->
isNetworkAvailable = online
logEvent("NETWORK_CHANGE", "Network: ${if (online) "online" else "offline"}")
if (online) syncBufferedLocations()
}
// MISSING: registerReceiver(connectivityReceiver, intentFilter)
Impact:
- Network state is never detected
- When the app goes offline, buffered locations are never synced when connectivity returns
- App never knows if it's online or offline
3. Timestamp Format Mismatch
File: android/app/src/main/kotlin/com/traccar/traccar_client/network/TraccarHttpClient.kt
Problem: The code sends timestamps as epoch milliseconds (e.g., 1704067200000), but Traccar expects ISO 8601 format (e.g., 2024-01-01T00:00:00.000Z).
// TraccarHttpClient.kt:55-61
return "$baseUrl/?" +
// ...
"×tamp=$timestamp" // Sends: 1704067200000
// Should send: 2024-01-01T00:00:00.000Z
Impact: Server may ignore or misinterpret timestamps, causing positions to not appear correctly on the timeline or be rejected entirely.
Important Bugs (From Code Review)
4. Memory Leak - Stream Subscription Never Cancelled
File: lib/main_screen.dart:52-67
Future<void> _initLocationStream() async {
// ...
LocationBridge.locationUpdates.listen((location) { // Subscription created
// ...
});
// MISSING: dispose() never cancels this subscription
}
Impact: Memory leak that accumulates over time, especially when navigating away and back to the screen.
5. Settings Number Fields Never Save
File: lib/settings_screen.dart:149-174
Problem: _buildNumberField() uses initialValue (which creates an internal controller) and onSaved callback. However, _saveSettings() never calls formKey.currentState.save(), so the onSaved callbacks at lines 161-172 never execute.
// settings_screen.dart
Future<void> _saveSettings() async {
await Preferences.setServerUrl(_serverUrlController.text);
await Preferences.setDeviceId(_deviceIdController.text);
// ... number fields use onSaved but form.save() is never called!
await LocationBridge.updateConfig({...});
}
Impact: Distance Filter, Update Interval, and Heartbeat settings are silently ignored when saving. The UI shows the new values but they aren't persisted.
6. Distance Filter May Block All Updates
File: lib/bridge/location_bridge.dart (Android side: DistanceFilterProcessor.kt)
Problem: With default distanceFilter = 75 meters, if the device is stationary or moving less than 75m, all locations are filtered out. The intervalFilter only serves as a fallback when distance filter would reject, not as a primary interval mechanism.
Impact: Stationary devices or devices in slow-moving traffic may not send any location updates.
Verification Steps
To diagnose where the flow breaks, check the Status/Logs screen in the app:
| Log Entry | Color | Meaning |
|---|---|---|
LOCATION |
Green | Location received from GPS |
SYNC |
Cyan | Location successfully sent to server |
ERROR |
Red | Failed to send location |
FILTERED |
Grey | Location rejected by distance/interval filter |
NETWORK_CHANGE |
White | Network state changed |
Questions:
- Do you see
LOCATIONentries? - Do you see
SYNCentries afterLOCATION? - Do you see any
ERRORentries? - Do you see
NETWORK_CHANGEentries?
Files Affected
Dart/Flutter Files
lib/main_screen.dart- Memory leak, optimistic togglelib/settings_screen.dart- Number fields not savinglib/status_screen.dart- OKlib/preferences.dart- OKlib/bridge/location_bridge.dart- OKlib/main.dart- OK
Android/Kotlin Files
android/.../location/HeartbeatScheduler.kt- Heartbeat never firesandroid/.../service/LocationTrackingService.kt- ConnectivityReceiver not registeredandroid/.../network/TraccarHttpClient.kt- Timestamp format wrong
Summary
The device appears offline because:
- Heartbeat locations are never sent - WorkManager process isolation breaks the callback mechanism
- Buffered locations are never synced - ConnectivityReceiver was never registered
- Timestamps may be malformed - Server may reject positions due to timestamp format
Recommendation: Fix the three critical bugs (Heartbeat, ConnectivityReceiver, Timestamp) first, then re-test. Check the Status/Logs screen to confirm which stage the flow is breaking at.