fix(gateway): surface error-only agent runs in webchat (restore lost fix)#2719
Merged
Conversation
…fix) When a webchat agent run produced only error payloads (e.g. auth failure, CLI crash) emitted as final-kind replies, the error was silently dropped: the deliver collector pushed all final text into finalReplyParts without distinguishing isError, and the post-run .then() broadcasts a final reply only for the non-agent-run path. broadcastChatError fired solely from .catch() (promise rejection), never for in-band error payloads — so an error-only agent run completed as "ok" with nothing surfaced to the UI. - Collect error text in a dedicated errorReplyParts array (kept out of finalReplyParts) and set hasErrorPayload before the empty-text early return so text-less error payloads still mark the run as errored. - In the post-run handler, when the agent run produced only error payloads (finalReplyParts empty, errorReplyParts non-empty), broadcast the combined error to the webchat UI. A mixed run (error + assistant text) is not misclassified as an error. This fix was previously merged then silently reverted by a later upstream sync that overwrote chat.ts; its error-propagation test was left behind and had been passing vacuously. The test is hardened with an error-only-run case that fails on the unfixed code. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
alexey-pelykh
enabled auto-merge (squash)
June 15, 2026 14:13
This was referenced Jun 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In the webchat chat handler, when an agent run produced only error payloads (e.g. auth failure, CLI crash) emitted as
final-kind dispatcher replies, the error was silently dropped:delivercollector pushed all final text intofinalReplyPartswithout distinguishingpayload.isError;.then()callsbroadcastChatFinalonly on the non-agent-run path (if (!agentRunStarted));broadcastChatErrorfired solely from.catch()(promise rejection), never for in-band error payloads.So an error-only agent run resolved normally, marked the run
ok, and surfaced nothing to the webchat UI.Fix (
src/gateway/server-methods/chat.ts)errorReplyPartsarray (kept out offinalReplyParts), and sethasErrorPayloadbefore the empty-text early return so text-less error payloads still mark the run as errored.finalReplyPartsempty,errorReplyPartsnon-empty), broadcast the combined error to the webchat UI. A mixed run (error + assistant text) is not misclassified — the assistant text still flows via the agent run's own stream.Provenance / regression context
This fix was previously merged and then silently reverted by a later upstream sync that overwrote
chat.ts(upstream never carried it). The accompanyingchat.error-propagation.test.tswas left behind and had been passing vacuously — its assertions held trivially against the broken code.Tests (
chat.error-propagation.test.ts)error-state broadcast carrying the error text, and nofinalbroadcast. Verified it fails on the unfixed code (1 failed / 3 passed) and passes with the fix (4/4) — a real regression guard, not vacuous.pnpm check(format +tsgotypecheck + lint + fork guards) passes.🤖 Generated with Claude Code