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.