tracpulse/docs/BUG_REPORT.md

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/?" +
    // ...
    "&timestamp=$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:

  1. Do you see LOCATION entries?
  2. Do you see SYNC entries after LOCATION?
  3. Do you see any ERROR entries?
  4. Do you see NETWORK_CHANGE entries?

Files Affected

Dart/Flutter Files

  • lib/main_screen.dart - Memory leak, optimistic toggle
  • lib/settings_screen.dart - Number fields not saving
  • lib/status_screen.dart - OK
  • lib/preferences.dart - OK
  • lib/bridge/location_bridge.dart - OK
  • lib/main.dart - OK

Android/Kotlin Files

  • android/.../location/HeartbeatScheduler.kt - Heartbeat never fires
  • android/.../service/LocationTrackingService.kt - ConnectivityReceiver not registered
  • android/.../network/TraccarHttpClient.kt - Timestamp format wrong

Summary

The device appears offline because:

  1. Heartbeat locations are never sent - WorkManager process isolation breaks the callback mechanism
  2. Buffered locations are never synced - ConnectivityReceiver was never registered
  3. 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.