Skip to content

fix(agents): surface session transport send failures to the caller - #2482

Open
u9g wants to merge 3 commits into
mainfrom
jason/remote-session-send-parity
Open

fix(agents): surface session transport send failures to the caller#2482
u9g wants to merge 3 commits into
mainfrom
jason/remote-session-send-parity

Conversation

@u9g

@u9g u9g commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Problem

RoomSessionTransport.sendMessage returned silently when the transport was closed or the room had disconnected, and swallowed write errors behind a warning. TcpSessionTransport.sendMessage returned silently when closed. Two consequences:

  • A RemoteSession request over a dead transport resolved as sent and then waited out its full timeout.
  • A SessionHost response that could not be delivered disappeared with nothing to correlate to the request.

Python's SessionTransport raises in both cases. Its comment states the contract: a dropped message is a request the caller waits out in full, so failures are raised rather than logged, because only the caller knows whether anyone is waiting on this one. See send_message in https://github.com/livekit/agents/blob/bbfcbcebe9eeac16120ddffdc166d55f59295211/livekit-agents/livekit/agents/voice/remote_session.py#L148-L169 and the TCP variant just below it.

Change

  • RoomSessionTransport.sendMessage throws room session transport is closed when closed or the room is disconnected, and rethrows write failures as failed to send binary stream message with the cause attached.
  • TcpSessionTransport.sendMessage throws tcp session transport is closed when closed or the socket is gone. A send already parked on backpressure still resolves when the transport closes, as before.
  • SessionHost.sendEvent catches the rejection and logs one failed to send session event warning, mirroring Python's write loop. Request handling needed nothing: handleRequestSafe already logs and its error-response retry already has a catch.

Tests

Three new tests in remote_session.test.ts: the room transport rejects on a disconnected room, the TCP transport rejects after close, and the host logs rather than leaving an unhandled rejection. All 23 tests in the file pass. The full src/voice directory passes except amd_session_close.test.ts, which needs a built dist/ and fails identically on an untouched checkout.

RoomSessionTransport.sendMessage returned silently when the transport was closed or the room disconnected, and swallowed write errors with a warning; TcpSessionTransport did the same when closed. A RemoteSession request that could not be sent therefore sat until its timeout, and a SessionHost response that could not be delivered vanished without a trace.

Both transports now throw, matching the Python SessionTransport: a dropped message is a request somebody waits out in full, so only the caller can decide what a failure means. The one caller nobody awaits, an event forwarded by SessionHost, logs a single warning instead.
@u9g
u9g requested a review from a team as a code owner September 11, 2026 16:54
@changeset-bot

changeset-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 049e817

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 39 packages
Name Type
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-anthropic Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-azure Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-krisp Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-meta Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-protoface Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugins-test Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

sendRequestRaw registered the pending future before sending and only removed it on a response or timeout, so a send that throws left the entry in the map until close(). Clean up in a finally, as the Python RemoteSession does.
@u9g

u9g commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

A RemoteSession request sent over a transport the host already closed, with a 5 second request timeout. Before: the send is swallowed and the request waits out the full timeout. After: the transport reports the failure and the request rejects at once with the reason.

transport-send-parity.mp4

TcpAudioOutput.flush and clearBuffer fire sendMessage without awaiting it.
Now that a closed TcpSessionTransport rejects, those calls became unhandled
rejections after console teardown. Log at debug instead, matching Python's
send_message_threadsafe which drops a send on a closed transport.
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.

1 participant