Skip to content

fix(signalr): don't panic on non-feed SignalR Core frames#317

Merged
slowlydev merged 2 commits into
slowlydev:mainfrom
lucvan:local-customizations
Jun 6, 2026
Merged

fix(signalr): don't panic on non-feed SignalR Core frames#317
slowlydev merged 2 commits into
slowlydev:mainfrom
lucvan:local-customizations

Conversation

@lucvan
Copy link
Copy Markdown
Contributor

@lucvan lucvan commented Jun 6, 2026

Problem

After F1's live timing moved to the SignalR Core endpoint (livetiming.formula1.com/signalrcore), the realtime service crashes its ingest task within ~15s of connecting, on every published image I tested (4.0.4, 4.0.5, develop, and latest rebuilt after the recent fix: invocation_id missing commit). The HTTP/SSE server keeps running, so the dashboard loads but shows no live data — the failure is silent.

thread 'tokio-rt-worker' panicked at signalr/src/lib.rs:282:
called `Result::unwrap()` on an `Err` value:
Error("missing field `target`", line: 1, column: 10)

Root cause

SignalR Core interleaves protocol frames among the feed invocations — most commonly ping frames {"type":6} (exactly the 10-char message in the panic above), plus completion/close frames. Two places assume a stricter frame shape:

  1. listen() .unwrap()s every frame into FeedMessage, which requires a target field. The first ping panics the worker and kills all live data.
  2. subscribe() assumes the immediate next frame after the Subscribe invocation is the Completion, so a ping arriving first breaks subscription.

Fix

  • listen(): skip frames that don't deserialize as a feed invocation instead of unwrapping (logged at debug).
  • subscribe(): read until the Completion whose invocationId matches ours, skipping ping/other frames that may arrive first.

No protocol or struct changes — purely defensive parsing.

Validation

Built from this branch and run against the live endpoint during an active session:

  • Connects to wss://livetiming.formula1.com/signalrcore, handshake + subscribe succeed.
  • 0 panics / 0 restarts over a full session (vs. crash within ~15s before).
  • /api/current serves real session state (full 44-driver list, weather, timing, ExtrapolatedClock/Heartbeat).
  • Ping frames now logged as skipping non-feed signalr frame at debug and ignored.

🤖 Generated with Claude Code

F1's livetiming signalrcore endpoint interleaves ping frames (`{"type":6}`)
and completion frames among the `feed` invocations. listen() unwrapped every
frame into FeedMessage (which requires `target`), so the first ping panicked
the tokio ingest worker and silently killed all live data while the HTTP
server kept serving empty connections.

- listen(): skip frames that don't deserialize as a feed invocation instead
  of unwrapping (logs at debug).
- subscribe(): read until the Completion matching our invocation id, skipping
  ping/other frames that may arrive first, instead of assuming the next frame
  is the Completion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lucvan lucvan requested a review from slowlydev as a code owner June 6, 2026 14:57
Copy link
Copy Markdown
Owner

@slowlydev slowlydev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks

@slowlydev slowlydev merged commit 4aa28d3 into slowlydev:main Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants