Summary
When a connection's transport reaches EOF (e.g. a ByteStreams peer closes, or the remote process dies), in-flight send_request futures are never resolved — they park forever. Nothing fails them with a transport-closed error, and the behavior isn't documented.
This matters because it is silently load-bearing in AcpAgent: its connect_to races the protocol future against a child monitor, so a dying agent process happens to tear everything down. But any consumer that brings its own transport via ByteStreams (the documented "sockets, pipes" use case) inherits the hang with no warning: an agent that dies mid-request leaves the caller's initialize/session/prompt awaiting forever.
Reproduction
- Connect a
Client over ByteStreams to a process that answers initialize, then exits 300ms later.
- Send
session/prompt (or any request) in that window.
- The request future never resolves — no error, no timeout — even though the incoming stream ended. (We hit this exactly while replacing
AcpAgent with our own spawn + ByteStreams; the fix on our side was re-creating the child-death race externally.)
Ask
Either (preferably both):
- Fail pending requests on transport end. When the incoming stream terminates, resolve every pending reply with a transport-closed error (and, ideally, have
connect_with complete). This matches what users of request/response RPC libraries generally expect, and would make the AcpAgent child-monitor race a redundancy instead of a hidden requirement.
- Document the current contract on
ByteStreams / connect_with: "transport EOF does not fail in-flight requests; callers must race the connection against their own liveness signal" — plus a cookbook example of doing so.
Happy to contribute either — (2) is trivial; for (1) I'd appreciate a pointer on whether failing pending_replies in the incoming actor's end-of-stream path is the intended place.
Environment
Summary
When a connection's transport reaches EOF (e.g. a
ByteStreamspeer closes, or the remote process dies), in-flightsend_requestfutures are never resolved — they park forever. Nothing fails them with a transport-closed error, and the behavior isn't documented.This matters because it is silently load-bearing in
AcpAgent: itsconnect_toraces the protocol future against a child monitor, so a dying agent process happens to tear everything down. But any consumer that brings its own transport viaByteStreams(the documented "sockets, pipes" use case) inherits the hang with no warning: an agent that dies mid-request leaves the caller'sinitialize/session/promptawaiting forever.Reproduction
ClientoverByteStreamsto a process that answersinitialize, then exits 300ms later.session/prompt(or any request) in that window.AcpAgentwith our own spawn +ByteStreams; the fix on our side was re-creating the child-death race externally.)Ask
Either (preferably both):
connect_withcomplete). This matches what users of request/response RPC libraries generally expect, and would make theAcpAgentchild-monitor race a redundancy instead of a hidden requirement.ByteStreams/connect_with: "transport EOF does not fail in-flight requests; callers must race the connection against their own liveness signal" — plus a cookbook example of doing so.Happy to contribute either — (2) is trivial; for (1) I'd appreciate a pointer on whether failing
pending_repliesin the incoming actor's end-of-stream path is the intended place.Environment
agent-client-protocol1.0.0 and currentmain.