fix: guard against missing default chat agent in sendRequest (fixes #321961)#321967
Open
vs-code-engineering[bot] wants to merge 1 commit into
Open
fix: guard against missing default chat agent in sendRequest (fixes #321961)#321967vs-code-engineering[bot] wants to merge 1 commit into
vs-code-engineering[bot] wants to merge 1 commit into
Conversation
…321961) ChatService.sendRequest used a non-null assertion on getDefaultAgent(location, mode), which returns IChatAgent | undefined. When no activated agent matches the location/mode, defaultAgent became undefined and flowed into the _onDidSubmitAgent event payload (typed non-nullable), crashing the chatInputEditorContrib listener at agentAndCommandToKey when reading agent.id. Mirror the existing guard in processNextPendingRequest: bail out with a rejected result and a logService.warn instead of asserting non-null. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Summary
ChatService.sendRequestreads the default chat agent with a non-null assertion —getDefaultAgent(location, options?.modeInfo?.kind)!— but that method is declared to returnIChatAgent | undefinedand genuinely returnsundefinedwhen no activated agent matches the requested location/mode. Theundefinedvalue then flows into the_onDidSubmitAgentevent payload (whoseagentfield is typed non-nullable), and thechatInputEditorContriblistener crashes readingagent.idinagentAndCommandToKey. This is a high-volume crash (298 hits / 149 users on 1.125.0).Fixes #321961
Recommended reviewer:
@roblourensCulprit Commit
getDefaultAgent(...)!) is a latent type-system bypass inchatServiceImpl.ts. The telemetry bucket has hits only for the shipped commit93cfdd48(1.125.0, FirstSeen 2026-06-17), so the history API yields no clean last-good/first-bad range, and the checkout is a depth-1 shallow clone, sogit blame/git logcannot attribute the line. The companion sibling code path (processNextPendingRequest) already guards this exact case, indicating thesendRequestsite simply missed the guard. The recent spike most plausibly correlates with mode-aware filtering ingetDefaultAgent(it rejects agents whosemodesdo not include the request mode), which can make the default-agent lookup returnundefinedfor a reachable mode — but no single triggering commit could be confirmed from the available data.Code Flow
sequenceDiagram participant User as User participant Service as ChatService.sendRequest participant Widget as ChatWidget participant Contrib as chatInputEditorContrib User->>Service: submit chat input Note over Service: ⚠️ Root cause:<br/>getDefaultAgent(location, mode)!<br/>returns undefined, asserted non-null Service->>Widget: sent result with data.agent = undefined Widget->>Contrib: _onDidSubmitAgent.fire({ agent: undefined }) Note over Contrib: 💥 TypeError:<br/>Cannot read properties of<br/>undefined (reading 'id')Affected Files
src/vs/workbench/contrib/chat/common/chatService/chatServiceImpl.tssendRequestusesgetDefaultAgent(...)!(line 1018), producing theundefinedagent.src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts_onDidSubmitAgent.fire({ agent: sent.data.agent })(emitter payload typed non-nullable).src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputEditorContrib.tsonDidSubmitAgentlistener callsagentAndCommandToKey(e.agent, ...), which readsagent.id.src/vs/workbench/contrib/chat/common/chatService/chatService.tsIChatSendRequestData.agent: IChatAgentData(non-nullable), the contract that the bypass violates.Repro Steps
getDefaultAgent(location, mode)returnsundefined. With no explicit@agentpart in the prompt and no silent agent,agentresolves toundefined.onDidSubmitAgentfires withagent: undefined.agentAndCommandToKeyreadsagent.id→TypeError: Cannot read properties of undefined (reading 'id').How the Fix Works
Chosen approach (
src/vs/workbench/contrib/chat/common/chatService/chatServiceImpl.ts): Remove the non-null assertion on thegetDefaultAgent(location, options?.modeInfo?.kind)call (the type-system bypass atchatServiceImpl.ts:1018) and add a guard clause that bails out early when no default agent is available:This fixes the data producer rather than guarding the crash site: the bypass is where the
IChatAgent | undefinedreturn is wrongly narrowed to non-null, so the guard belongs there. After the guard,defaultAgentisIChatAgent, so the downstreamagentvalue and the_sendRequestAsyncargument can never beundefined, and the non-nullable_onDidSubmitAgentpayload contract is honored — the crash-site listener can never receive anundefinedagent. The fix mirrors the existing guard in the sibling methodprocessNextPendingRequestin this same file, which already handles the identical "no default agent" case with a rejected result and alogService.warn. The violation is surfaced (warning + rejected result), not silently swallowed, so error telemetry is preserved.Alternatives considered:
agentAndCommandToKeyor theonDidSubmitAgentlistener (the crash site) — rejected because it coerces/suppresses at the consumer and hides the producer bug; the event payload is declared non-nullable, so the violating value must be stopped upstream.chatWidget.ts— rejected for the same reason: it masks an invalid request state instead of rejecting it where the request is assembled.Recommended Owner
@roblourens— Intriage/working-areas.mdhe owns "ChatModel creation, management, persistence" and "Chat participant API", which map tochatServiceImpl.ts(the producer/fix site). He previously fixed the analogous "don't throw when the agent is gone" crash in this same file (PR #313068). Team-membership gate: passes (core maintainer with write access, listed in working-areas.md). Liveness gate: passes (multiple commits on 2026-06-10, well within 90 days).