Skip to content

fix: reject replies from retired streamable http sessions - #1206

Open
nickcoai wants to merge 1 commit into
mainfrom
agent/streamable-http-session-origin
Open

fix: reject replies from retired streamable http sessions#1206
nickcoai wants to merge 1 commit into
mainfrom
agent/streamable-http-session-origin

Conversation

@nickcoai

@nickcoai nickcoai commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix the late-handler reply issue from Dale's review.

This PR is based on main, independently of the worker refactor in #1205. The late-handler routing issue predates #1186. Whichever follow-up lands second will need a rebase to combine their transport changes.

  • Record the local transport and session generation when an inbound request is handed to the service.
  • Carry that origin through automatic responses and errors. Do not send an old handler's reply to a replacement session, including during shutdown draining.
  • Check handler identity when completing a request. If a new session reuses its request id, the old handler cannot remove or cancel the new one.
  • Keep the origin local. There are no wire-format changes or new public transport methods.

Scope

This protects automatic service replies, including request-association errors. Manually constructed raw transport replies have no captured origin.

Transport wrappers must preserve request extensions. Wrappers that defer the inner send within the same future retain the reply context; detached tasks must construct the inner send before spawning. Serializing requests or deferring send construction to a detached task loses the private context.

The tests cover late success and error replies, reused request ids, shutdown draining, and recovery during a pending reply send.

Validation

Validated at 8477d25, rebased onto main at 3b5ca4d:

  • 245 unit tests and 24 concurrency tests with default features plus client and transport-streamable-http-client.
  • All four new late-handler regressions fail on main at 3b5ca4d and pass with this fix.
  • Production feature checks for the client without a worker, the client with the generic worker, and the local-mode HTTP client.
  • Nightly formatting and diff checks.

Full all-features tests, clippy, and downstream adapter tests were not rerun after this rebase.

@github-actions github-actions Bot added T-test Testing related changes T-core Core library changes T-transport Transport layer changes labels Aug 23, 2026
@nickcoai
nickcoai marked this pull request as ready for review August 23, 2026 15:10
@nickcoai
nickcoai requested a review from a team as a code owner August 23, 2026 15:10
@nickcoai
nickcoai requested a review from DaleSeo August 23, 2026 15:10
Base automatically changed from agent/configurable-streamable-http-parallelism to main August 24, 2026 13:43
Keep the inbound transport generation with automatic handler responses
and errors. Reject replies after session recovery, including during the
shutdown drain, without adding wire metadata.

Match the completing handler's token before removing its request id so
an old handler cannot cancel a replacement that reuses the id.

Cover late replies, id reuse, scoped send context, and shutdown races.
@nickcoai
nickcoai force-pushed the agent/streamable-http-session-origin branch from 6e6416c to 8477d25 Compare September 2, 2026 22:08
Comment on lines +1339 to +1341
.extensions()
.get::<crate::transport::worker::ResponseOrigin>()
.cloned(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should take the origin out at capture instead. ReplyContext::from_request only clones it, so the token rides mem::swap into the public RequestContext::extensions.

Suggested change
.extensions()
.get::<crate::transport::worker::ResponseOrigin>()
.cloned(),
.extensions_mut()
.remove::<crate::transport::worker::ResponseOrigin>(),

handlers.remove(id);
true
} else {
false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old Event::ToSink arm logged before discarding a response.

Suggested change
false
tracing::debug!(%id, "dropping response: request was cancelled or its id was reused");
false

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-test Testing related changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants