Skip to content

fix(vehicle): keep the stream connected while a drive is interrupted by an API outage - #5647

Open
onevcat wants to merge 2 commits into
teslamate-org:mainfrom
onevcat:fix/keep-stream-while-driving-unavailable
Open

fix(vehicle): keep the stream connected while a drive is interrupted by an API outage#5647
onevcat wants to merge 2 commits into
teslamate-org:mainfrom
onevcat:fix/keep-stream-while-driving-unavailable

Conversation

@onevcat

@onevcat onevcat commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

What happens today

When a fetch reports the vehicle as offline or asleep, the fetch-result handler always disconnects the stream — including in the middle of a drive.

The Data/Fleet API and the streaming socket fail independently. A vehicle that the API cannot reach is very often still streaming: a cellular ↔ Wi-Fi handover during a short stop, a weak-coverage stretch, or a slow Tesla backend is enough to get vehicle_data -> 408, which the state machine treats as offline. The car keeps pushing positions, but the socket has just been torn down, so everything until the next successful vehicle_data is discarded.

A concrete case from a self-hosted Fleet Telemetry setup (the code path is the same on main):

  • The car stops at home for 10 minutes and switches from cellular to home Wi-Fi. The park-confirmation fetch hits that moment and returns 408 → Vehicle went offline while drivingStream disconnecting ..., then 30-second retries, all 408.
  • The car drives off 8 minutes later. Telemetry keeps arriving at the telemetry server the whole time.
  • TeslaMate records its next position 9 minutes after that, when a fetch finally succeeds — about 2.7 km of the drive is a straight line on the map.

The window is as long as it takes for one vehicle_data call to succeed. Because every one of those calls is billed on the Fleet API, installations that raise POLLING_MINIMUM_INTERVAL to contain 408 storms make it correspondingly longer; in the case above it was 15 minutes.

What this PR changes

  1. The offline/asleep fetch-result clause no longer disconnects the stream while the state machine is in a driving state. Its comment says the teardown is there for the asleep/offline "real online" probe, and that is still exactly what it does — every path that ends in Start / :offline or Start / :asleep disconnects, and Start / :online reuses an existing stream_pid, so no socket is leaked.
  2. The driving stream handler inserts D/N/R frames into the open drive in every driving status, not only :available. The existing {_status, %Stream.Data{}} branch already anticipated frames arriving while the vehicle is unavailable — it just never received any, because the socket was always gone by then.
  3. While unavailable/offline, the remaining frames no longer schedule_fetch(0). The offline handlers already have a fetch scheduled, and one request per frame would only add vehicle_data calls that are known to fail right now; with a raised POLLING_MINIMUM_INTERVAL it would also keep re-arming the pending state timeout and push the real fetch further out.

Deliberately unchanged: a fetch remains the only thing that can bring the status back to :available. A stale or replayed frame can therefore, at worst, insert an out-of-order position — the same exposure that exists in :available today — and can never flip the state machine back and forth. The 15-minute drive timeout in {:driving, {:offline, _}, _} is untouched, and after it fires the drv == nil guard stops further frames from being written.

Relation to #5535

#5535 (fixes #5534, reported in #5532) reconnects the stream after the vehicle is back online, so the remainder of the drive is streamed again and carries elevation. That is still needed for a stream process that actually died, and maybe_reconnect_stream/1 stays in place as that safety net.

This PR addresses the other half: the data that was streamed during the outage. With the socket kept open, those positions land in the drive as they arrive instead of being lost until the API recovers.

Tests

  • The two tests added in fix: reconnect stream when a drive resumes after mid-drive offline phase to avoid missing elevation  #5535 and logs a drive after a significant offline period while driving now assert that the stream is neither disconnected nor reconnected during the offline phase.
  • A new streaming test covers the whole sequence: a drive recorded from the stream → the API reports the vehicle offline → a D frame is still recorded into the drive, a P frame is ignored → the API sees the vehicle again and the drive continues on the same socket.

mix format --check-formatted, mix compile --warnings-as-errors and the vehicle test suite pass locally.

…by an API outage

When a fetch reported the vehicle offline or asleep, the stream was always
torn down, including mid-drive. There the vehicle is usually only
unreachable for the API for a while, and the stream keeps delivering
positions; those were discarded until the next successful fetch, leaving a
hole in the drive. teslamate-org#5535 already reconnects the stream once the vehicle is
back online, but everything streamed in between was lost.

Keep the stream while the state machine is in a driving state and record
D/N/R frames into the open drive also while it is unavailable or offline.
The fetch scheduled by the offline handlers stays the only way back to
:available; other frames no longer schedule a fetch in those states, since
every one of them would fail the same way.
@netlify

netlify Bot commented Aug 24, 2026

Copy link
Copy Markdown

Deploy Preview for teslamate ready!

Name Link
🔨 Latest commit 966a8ef
🔍 Latest deploy log https://app.netlify.com/projects/teslamate/deploys/6a8c63aa672f3b0008a3840f
😎 Deploy Preview https://deploy-preview-5647--teslamate.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@onevtail

Copy link
Copy Markdown

While validating exact head 966a8ef25c60abf759af4ce1575862cd5f144aba, I found an edge case in the claimed offline/asleep coverage.

The offline path works as intended: the stream remains available, D/N/R frames continue to update the existing drive, and ordinary frames do not postpone the scheduled fetch. However, an asleep result while driving preserves stream_pid, then the subsequent :asleep event immediately calls timeout_drive/2 and transitions to :start. Later D frames therefore miss the driving handler and are ignored, while the socket can remain connected until a later successful fetch disconnects it.

I reproduced this with streaming enabled: the initial asleep result closed the drive; while the next fetch was blocked, no disconnect occurred and an injected D frame produced no insert_position; releasing that fetch then caused the disconnect.

The minimal correction appears to be either:

  1. Preserve the stream only for offline, retaining the existing asleep teardown and narrowing the PR description; or
  2. Route asleep while driving through the unavailable/offline recovery flow so the existing drive timeout determines when the drive closes.

A regression test with use_streaming_api: true should cover the drive lifecycle, D-frame insertion, and socket lifecycle for the chosen behavior. The current exact head does not yet fully handle the asleep case.

onevtail - an assistant to @onevcat

@onevcat

onevcat commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

The review above is technically valid, but the scenario cannot occur in practice. While driving, the vehicle may be reported as offline/unavailable due to network conditions, and without this fix, trip data received from the stream during subsequent connectivity checks can indeed be lost. However, the vehicle should never transition to asleep while it is being driven.

Therefore, I think we should keep the current fix as is.

@JakobLichterfeld JakobLichterfeld added note:discussion Details or approval are up for discussion undetermined Not sure if this is considered a real bug labels Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

note:discussion Details or approval are up for discussion undetermined Not sure if this is considered a real bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stream is not reconnected when a drive resumes after a mid-drive offline phase (rest of drive falls back to polling, missing elevation)

3 participants