{"record":{"id":"4af6fd95b6a64b11","repo":"microsoft/vscode","slug":"only-plan-agent-is-supported","errorCode":null,"errorMessage":"Only \"Plan\" agent is supported","messagePattern":"Only \"Plan\" agent is supported","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/copilot/src/extension/tools/vscode-node/switchAgentTool.ts","lineNumber":33,"sourceCode":"interface ISwitchAgentParams {\n\tagentName: string;\n}\n\nexport class SwitchAgentTool implements ICopilotTool<ISwitchAgentParams> {\n\tpublic static readonly toolName = ToolName.SwitchAgent;\n\tpublic static readonly nonDeferred = true;\n\n\tconstructor(\n\t\t@IConfigurationService private readonly configurationService: IConfigurationService,\n\t\t@IExperimentationService private readonly experimentationService: IExperimentationService,\n\t) { }\n\n\tasync invoke(options: vscode.LanguageModelToolInvocationOptions<ISwitchAgentParams>, token: CancellationToken): Promise<vscode.LanguageModelToolResult> {\n\t\tconst { agentName } = options.input;\n\n\t\t// Only 'Plan' is supported\n\t\tif (agentName !== 'Plan') {\n\t\t\tthrow new Error(vscode.l10n.t('Only \"Plan\" agent is supported'));\n\t\t}\n\n\t\tconst exploreEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.ExploreAgentEnabled, this.experimentationService);\n\t\tconst searchSubagentEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.Advanced.SearchSubagentToolEnabled, this.experimentationService);\n\t\tconst planAgentBody = PlanAgentProvider.buildAgentBody(exploreEnabled, searchSubagentEnabled);\n\n\t\t// Execute command to switch agent\n\t\tawait vscode.commands.executeCommand('workbench.action.chat.toggleAgentMode', {\n\t\t\tmodeId: agentName,\n\t\t\tsessionResource: options.chatSessionResource\n\t\t});\n\n\t\treturn new LanguageModelToolResult([\n\t\t\tnew LanguageModelTextPart(`Switched to ${agentName} agent. You are now the ${agentName} agent. This tool may no longer be available in the new agent.\\n\\n${planAgentBody}`)\n\t\t]);\n\t}\n\n\tprepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<ISwitchAgentParams>, token: vscode.CancellationToken): vscode.ProviderResult<vscode.PreparedToolInvocation> {","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/microsoft/vscode/blob/a94b963a32aa9749d69504576791fa29700ae46d/extensions/copilot/src/extension/tools/vscode-node/switchAgentTool.ts#L15-L51","documentation":"The SwitchAgentTool is a VS Code Language Model Tool that the chat agent (LLM) invokes to switch the active Copilot agent mode. Its invoke() method hard-rejects any agentName other than the literal string 'Plan', because the command 'workbench.action.chat.toggleAgentMode' and the PlanAgentProvider body it injects are only wired for the Plan agent. The ISwitchAgentParams interface types agentName as a plain string, so the type system does not constrain the value — the runtime guard at line 32 is the only enforcement.","triggerScenarios":"The chat model emits a switchAgent tool call with options.input.agentName set to anything except 'Plan' (e.g. 'Edit', 'Ask', 'code', an empty string, or a hallucinated mode id). This happens during invoke(), after prepareInvocation() has already passed. The throw originates from the equality check `if (agentName !== 'Plan')` at switchAgentTool.ts:32.","commonSituations":"The LLM hallucinates a mode name not in the allowed set; a custom prompt/instruction tells the model to switch to a non-Plan agent; the agent registry was extended with new modes but SwitchAgentTool was not updated; case sensitivity bites ('plan' vs 'Plan'); an automated test or simulation drives the tool directly with an arbitrary string.","solutions":["Ensure the tool is only called with agentName exactly equal to 'Plan' (case-sensitive) — this is the only accepted value.","If you are driving the tool from a test or simulation, pass { agentName: 'Plan' } as options.input.","If you need to support a new agent mode, extend the guard at switchAgentTool.ts:32 and wire the corresponding toggleAgentMode modeId plus an agent body provider; do not bypass the check.","Audit any custom system prompts or tool-result injections that instruct the model to switch agents, and remove references to non-Plan modes."],"exampleFix":"// before\nconst input = { agentName: modelDecidedMode };\nawait switchAgentTool.invoke({ input, ... }, token);\n\n// after — constrain the caller to the only supported value\nconst SUPPORTED_AGENT = 'Plan';\nconst input = { agentName: SUPPORTED_AGENT };\nawait switchAgentTool.invoke({ input, ... }, token);","handlingStrategy":"validation","validationCode":"const SUPPORTED_SWITCH_AGENT = 'Plan' as const;\nfunction isValidSwitchAgentName(name: unknown): name is typeof SUPPORTED_SWITCH_AGENT {\n\treturn name === SUPPORTED_SWITCH_AGENT;\n}\n\n// before invoking:\nif (!isValidSwitchAgentName(options.input.agentName)) {\n\treturn new LanguageModelToolResult([\n\t\tnew LanguageModelTextPart(`Cannot switch agent. Only \"${SUPPORTED_SWITCH_AGENT}\" is supported.`)\n\t]);\n}\nawait switchAgentTool.invoke(options, token);","typeGuard":"function isSupportedAgentName(name: string): name is 'Plan' {\n\treturn name === 'Plan';\n}","tryCatchPattern":"try {\n\tawait switchAgentTool.invoke(options, token);\n} catch (e) {\n\tconst msg = e instanceof Error ? e.message : String(e);\n\tif (msg.includes('Only \"Plan\" agent is supported')) {\n\t\t// surface a model-friendly correction rather than crashing the turn\n\t\treturn new LanguageModelToolResult([new LanguageModelTextPart('Only the \"Plan\" agent can be selected.')]);\n\t}\n\tthrow e;\n}","preventionTips":["Treat agentName as the literal union 'Plan', not an open string, in any caller or test harness.","Constrain tool descriptions/system prompts so the model only ever proposes 'Plan'.","Add a unit test that asserts non-'Plan' values are rejected by both invoke and prepareInvocation."],"tags":["vscode","copilot","agent-mode","language-model-tool","validation"],"analyzedSha":"a94b963a32aa9749d69504576791fa29700ae46d","analyzedAt":"2026-08-12T01:08:56.794Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}