Skip to content

fix(http): Isolate session stream conflicts from shared transport - #303

Merged
benbrandt merged 4 commits into
agentclientprotocol:mainfrom
federicociner:codex/isolate-session-stream-failures
Oct 22, 2026
Merged

benbrandt merged 4 commits into
agentclientprotocol:mainfrom
federicociner:codex/isolate-session-stream-failures

Conversation

@federicociner

@federicociner federicociner commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A session-scoped HTTP 409 currently terminates the shared HTTP transport, making unrelated sessions on the same connection unusable.

This occurs when a server rejects a session stream’s saved position and requires an explicit reload. The conflict applies to that session, but openSse() calls errorReadable(), which aborts all streams and deletes the connection. Rejecting the corresponding writable operation can also leave the shared writer permanently errored.

This change:

  • Reports session-scoped SSE and POST 409s as correlated JSON-RPC errors without terminating the shared transport.
  • Retains the failed session’s stream error so subsequent requests fail promptly.
  • Allows an explicit session/load or session/resume to detach the failed stream and attempt a fresh stream attachment.
  • Fails only the affected session's pending requests when its SSE stream closes mid-request, instead of terminating the shared transport.
  • Preserves existing handling for connection-scoped failures and other transport errors.

Requests and prompts are not automatically replayed.

Reproduction

We reproduced the failure using the real ACP SDK with a controlled fetch implementation:

  1. Initialize one connection.
  2. Start a request for session A and leave its response pending.
  3. Return HTTP 409 when opening session B’s SSE stream, with an application-specific response: {"code":"session_reload_required"}
  4. Attempt another request on the same connection.

Before the fix, B’s conflict terminates the shared transport. Other sessions fail, and a client retaining that transport cannot use it for subsequent sessions. Creating a fresh transport succeeds.

With the fix, B receives a correlated error, A’s pending request completes, and a new session C works on the original connection. No connection DELETE occurs. An explicit load can retry B.

@federicociner
federicociner marked this pull request as ready for review October 21, 2026 01:41
benbrandt and others added 2 commits October 22, 2026 12:15
- Give each session SSE stream its own AbortController so a session that
  fails on a POST 409 is fully detached; a later reattach opens a fresh
  stream instead of reusing the stale one.
- Clear the failure on session/resume as well as session/load (v2 has no
  session/load).
- Keep response routing for failed sessions so late responses to server
  requests are discarded instead of being posted without a session header,
  which could still tear down the shared connection.
- Only clear session stream entries owned by the closing stream.
- Parse JSON error bodies into the synthesized error's data.
- Add tests for SSE and POST 409 conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A session SSE stream closing while requests are in flight previously tore
down the shared transport. Now only that session's pending requests fail
with JSON-RPC errors, and the next session message reopens the stream.

Also document session-scoped failure handling on createHttpStream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@benbrandt
benbrandt enabled auto-merge (squash) October 22, 2026 10:22
@benbrandt
benbrandt merged commit ad220c7 into agentclientprotocol:main Oct 22, 2026
2 checks passed
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