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

    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