fix: guard addStreamMessage with isStreaming() to avoid stream-completed race#1208
Open
HaiyiMei wants to merge 1 commit into
Open
fix: guard addStreamMessage with isStreaming() to avoid stream-completed race#1208HaiyiMei wants to merge 1 commit into
HaiyiMei wants to merge 1 commit into
Conversation
…ted race After cyrusagents#1141 started calling streamingPrompt.complete() on result message under non-warm mode, there is a brief window where isRunning() still returns true but streamingPrompt.completed is already true. Webhook handlers that called existingRunner.addStreamMessage(...) on the basis of isRunning() alone would throw "Cannot add message to completed stream", silently dropping the user's message after Cyrus had already posted "I've queued up your message as guidance". IAgentRunner.isStreaming?() is documented in agent-runner-types.ts as "Use this to guard calls to addStreamMessage()". ChatSessionHandler.ts already does this. Add it at the four EdgeWorker.ts sites that did not: - handlePromptWithStreamingCheck (the main prompted-webhook entry) - resumeAgentSession (early-return when stream is alive) - issue-update streaming delivery (CYPACK-954 path) - base-branch change notification streaming When the guard fails, callers fall through to query({resume: ...}) and the user's comment is delivered via the normal resume path instead of being lost. Test mock factory for the issue-update path updated to mirror isRunning into isStreaming so the streaming branch is reachable. Repro: send a comment in an active agent session thread; if it arrives in the window between SDK result emission and runner teardown (~tens of ms), Cyrus acknowledges "queued up as guidance" but logs the throw and drops the message. With this fix the comment falls through to resume.
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.
Bug
After #1141 began calling
streamingPrompt.complete()on the SDK result message under non-warm mode, a brief window opens where:ClaudeRunner.isRunning()still returnstrue(becausesessionInfo.isRunningis flipped after the for-await loop exits — seeClaudeRunner.ts:806)streamingPrompt.completedis alreadytrueWebhook handlers that guarded
addStreamMessage(...)withisRunning()alone passed the check, thenStreamingPrompt.addMessage()threw"Cannot add message to completed stream". The exception was logged but not caught, so the user's comment was silently dropped — after Cyrus had already posted"I've queued up your message as guidance"in the Linear UI. Confusing failure mode.Symptom from production logs
User's view: they replied to an agent session, saw "queued as guidance", waited 9 minutes for the agent to act on it, nothing happened. The comment had to be re-posted to take effect.
Fix
IAgentRunner.isStreaming?()is already part of the interface — seeagent-runner-types.ts:296:EdgeWorker.ts:1512— base-branch change notification streamingEdgeWorker.ts:3521— issue-update streaming delivery (CYPACK-954 path)EdgeWorker.ts:6619—handlePromptWithStreamingCheck(main prompted-webhook entry)EdgeWorker.ts:6706—resumeAgentSessionearly-return when stream is aliveWhen the guard fails, callers fall through to the proper
query({ resume: ... })path, preserving the user's message instead of dropping it.Test changes
EdgeWorker.issue-update-multiple-sessions.test.ts— the mock factory at line 84 was missingisStreamingon the agent runner mock. Other test files already mock both. AddedisStreaming: vi.fn().mockReturnValue(isRunning)so the streaming branch is reachable in tests.Verification
pnpm --filter cyrus-edge-worker test:run).pnpm --filter cyrus-edge-worker typecheckclean.pnpm buildclean across the workspace.Related
c0495f3d) — introduced thestreamingPrompt.complete()on result behavior under non-warm mode. This PR is a follow-up to harden the call sites that the change exposed.