Skip to content

Recover an interrupted stream

operationspreview
TL;DR

Preserve the exact opaque cursor when reconnecting an SSE subscription. Catch-up is bounded and may require a fresh application snapshot. Close on refresh-required, and do not retry the same exhausted cursor. A delivered event does not prove durable application processing; design reconciliation and token renewal into the consumer.

State machineReconnect is a state transition, not a blind retry loop
Reconnect is a state transition, not a blind retry loopLive delivery can disconnect. Reconnecting with a cursor enters replay, which returns to live delivery when retained data is available. A refresh-required event moves the client to rebuild; only a new subscription strategy returns it to live. Authorization failures require fixing access.LiveProcess complete batchesDisconnectedRetain last processedcursorReplayingLast-Event-ID on reconnectRefresh requiredClose the old streamRebuild stateAuthorized read + newstartConnection lostReconnectReplay caught upCannot resumeRefresh local stateNew subscriptionReconnect is a state transition, not a blind retry loopLive delivery can disconnect. Reconnecting with a cursor enters replay, which returns to live delivery when retained data is available. A refresh-required event moves the client to rebuild; only a new subscription strategy returns it to live. Authorization failures require fixing access.LiveProcess completebatchesDisconnectedRetain last processedcursorReplayingLast-Event-ID onreconnectRefresh requiredClose the old streamRebuild stateAuthorized read + newstartConnection lostReconnectReplay caught upCannot resumeRefresh local stateNew subscription
Fix authentication failures before reconnecting. A network or authorization failure can close the stream without a refresh-required event.
Message sequenceReconnect with a cursor, or rebuild after refresh-required
Reconnect with a cursor, or rebuild after refresh-requiredA disconnected consumer reconnects with its last retained opaque cursor. The server either replays retained events and resumes live delivery, or requests a refresh. The refresh path requires a fresh authorized read and subscription strategy.SSE consumerBoilStreamGET with Last-Event-IDRetained batches, then live eventsOR terminal refresh-requiredClose; rebuild from authorized dataTime moves downward · dashed arrows are responsesReconnect with a cursor, or rebuild after refresh-requiredA disconnected consumer reconnects with its last retained opaque cursor. The server either replays retained events and resumes live delivery, or requests a refresh. The refresh path requires a fresh authorized read and subscription strategy.SSE consumerBoilStreamGET with Last-Event-IDRetained batches, thenlive eventsOR terminalrefresh-requiredClose; rebuild fromauthorized dataTime moves downward · dashed arrows are responses
Replay has bounds. Invalid cursors and unavailable replay can require refresh even after HTTP 200 has begun.
  1. EventSource resends the exact last event ID in Last-Event-ID.
  2. The server authenticates the original stream token and binds the cursor to the original topic and table activation. A cursor never grants read access.
  3. The server replays unacknowledged complete batches from the retained table when it can continue the subscription within its resource limits.
  4. Replay completes before live delivery resumes. The server skips batches already covered by the cursor.

Do not construct cursors or convert them to numbers. Numeric IDs from the older preview protocol are rejected rather than interpreted as a new subscription. Malformed or foreign cursors fail explicitly. Capacity exhaustion or loss of a continuous stream requires refresh; unavailable replay requires a refresh instead of appearing to succeed with no data.

A transport cursor acknowledges a complete SSE event, not your application’s processing or durable side effects. An application may still need idempotent processing after reconnecting. New subscriptions intentionally skip the existing baseline, and snapshot refresh plus resubscription is not an atomic durable changefeed API. Use a canonical DuckLake query when a durable result is required; see the interface matrix.