Skip to content

[Bug]: SSE reattach with a cursor older than the event ring buffer silently drops eventsΒ #5411

Description

@tomsen02

What happened?

An SSE client that reconnects to GET /api/runs/:id/events with a Last-Event-ID / ?after=<n> cursor older than the in-memory event ring buffer silently loses every event the buffer already evicted β€” with no gap signal.

run.events is a bounded ring buffer (createChatRunService maxEvents = 2000). stream() replays only the surviving records whose id is greater than the cursor. On a run that has emitted more than 2000 events, the earliest events have been spliced out of memory, so a client reconnecting from a stale cursor jumps straight to the oldest surviving id and never receives the events in between. The full history is still in the on-disk events.jsonl; only the in-memory replay is lossy.

Steps to reproduce

Reproduced end-to-end against a real daemon over the production HTTP API, with a fake claude CLI that streams past the ring buffer:

  1. Point the daemon at a fake claude binary that emits ~2500 text_delta stream events, then exits 0.
  2. Start a run (POST /api/runs) and wait for it to finish. The on-disk events.jsonl has ~2506 events; run.events in memory has been truncated to the last 2000.
  3. Reattach: GET /api/runs/:id/events?after=5 (with Last-Event-ID: 5) β€” i.e. "give me everything after event 5".

Observed on main:

  • First replayed event id is 507, not 6.
  • Events 6–506 (501 events) are absent from the stream.
  • No events_truncated / gap signal is emitted β€” the client sees a contiguous-looking stream and cannot tell anything was dropped.

Expected behavior

The reattach resumes at cursor+1 and is gapless: the client receives every event after its cursor, in order, exactly as if it had never disconnected. Since the evicted events are still on disk, the daemon can serve them.

Open Design version

0.14.1

Platform

macOS (Apple Silicon)

Additional context

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions