Skip to content

Bug: Resumption token dropped when a resumed SSE stream disconnects before any id-bearing event #2499

Description

@ayush00git

Bug

In StreamableHTTPClientTransport, _handleSseStream() tracks the latest event id in a local lastEventId initialized to undefined. it is never seeded from the resumptionToken the stream was opened with. Both reconnect paths pass resumptionToken: lastEventId when scheduling the next attempt.

So if a stream resumed with Last-Event-ID: e1 disconnects again before any id-bearing event arrives (LB idle timeout, server restart), the reconnect GET is sent with no Last-Event-ID header. The server treats it as a brand-new standalone stream instead of
a resumption, missed events are never replayed and a long-running request hangs until timeout.

Repro

  1. Open a stream with resumptionToken: 'event-1' (GET carries Last-Event-ID: event-1 )
  2. Server accepts (200, text/event-stream) but the stream closes before any event with an id
  3. The scheduled reconnect GET carries no Last-Event-ID header.

Verified with a failing unit test: mock the resumed GET with a body that closes immediately,
then assert the second fetch call's headers and last-event-id is null.

Suggested fix

Seed the tracker from the options the stream was opened with:

let lastEventId: string | undefined = options.resumptionToken;

so a reconnect that saw no new events re-sends the same token (replay is idempotent)
instead of silently dropping it.
Happy to submit a PR with the fix + regression test.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions