Skip to content

fix: propagate input stream errors through ndJsonStream#111

Merged
benbrandt merged 3 commits intoagentclientprotocol:mainfrom
michelTho:fix/ndjson-stream-error-propagation
Apr 8, 2026
Merged

fix: propagate input stream errors through ndJsonStream#111
benbrandt merged 3 commits intoagentclientprotocol:mainfrom
michelTho:fix/ndjson-stream-error-propagation

Conversation

@michelTho
Copy link
Copy Markdown
Contributor

Problem

ndJsonStream uses try/finally to read from the input byte stream. When the input stream errors (e.g. a child process crashes and the ReadableStream controller calls controller.error()), the finally block calls controller.close() on the output message stream, swallowing the error. This causes Connection.#receive() to see a clean EOF instead of the actual error.

As a result, when a process crashes mid-connection:

  • Pending sendRequest() calls hang forever (before v0.18.0) or get a generic "ACP connection closed" error (v0.18.0+) instead of the actual crash error
  • The signal.reason / closed promise don't carry the original error

Fix

Replace try/finally with try/catch/finally:

  • catch: calls controller.error(err) to propagate the error to the message-level ReadableStream, then returns early
  • finally: always calls reader.releaseLock()
  • controller.close() only runs on the success path (after the try block)

Tests

Added two tests:

  • propagates input stream errors through ndJsonStream — verifies the connection closes when the input stream errors immediately
  • rejects pending requests when input stream errors via ndJsonStream — verifies in-flight requests are rejected with the original error message

The readable stream returned by ndJsonStream used try/finally, which
swallowed errors from the underlying input stream. When the input
errored (e.g. a child process crash), the readable stream would close
cleanly instead of erroring, causing the Connection to treat it as a
normal shutdown rather than propagating the error to pending requests.

Changed to try/catch/finally so that input stream errors are forwarded
via controller.error(), allowing Connection.#receive() to capture the
error and reject all pending requests with the original error message.

Added two tests:
- Verifies ndJsonStream propagates immediate input stream errors
- Verifies pending requests are rejected when input stream errors

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Copy link
Copy Markdown
Member

@benbrandt benbrandt left a comment

Choose a reason for hiding this comment

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

Thanks!

@benbrandt benbrandt enabled auto-merge (squash) April 8, 2026 11:33
@benbrandt benbrandt merged commit f57a8d1 into agentclientprotocol:main Apr 8, 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