microsoft/vscode · error · Error
Only "Plan" agent is supported
Error message
Only "Plan" agent is supported
What it means
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.
Source
Thrown at extensions/copilot/src/extension/tools/vscode-node/switchAgentTool.ts:33
interface ISwitchAgentParams {
agentName: string;
}
export class SwitchAgentTool implements ICopilotTool<ISwitchAgentParams> {
public static readonly toolName = ToolName.SwitchAgent;
public static readonly nonDeferred = true;
constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExperimentationService private readonly experimentationService: IExperimentationService,
) { }
async invoke(options: vscode.LanguageModelToolInvocationOptions<ISwitchAgentParams>, token: CancellationToken): Promise<vscode.LanguageModelToolResult> {
const { agentName } = options.input;
// Only 'Plan' is supported
if (agentName !== 'Plan') {
throw new Error(vscode.l10n.t('Only "Plan" agent is supported'));
}
const exploreEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.ExploreAgentEnabled, this.experimentationService);
const searchSubagentEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.Advanced.SearchSubagentToolEnabled, this.experimentationService);
const planAgentBody = PlanAgentProvider.buildAgentBody(exploreEnabled, searchSubagentEnabled);
// Execute command to switch agent
await vscode.commands.executeCommand('workbench.action.chat.toggleAgentMode', {
modeId: agentName,
sessionResource: options.chatSessionResource
});
return new LanguageModelToolResult([
new 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}`)
]);
}
prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<ISwitchAgentParams>, token: vscode.CancellationToken): vscode.ProviderResult<vscode.PreparedToolInvocation> {View on GitHub (pinned to a94b963a32)
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.
Example fix
// before
const input = { agentName: modelDecidedMode };
await switchAgentTool.invoke({ input, ... }, token);
// after — constrain the caller to the only supported value
const SUPPORTED_AGENT = 'Plan';
const input = { agentName: SUPPORTED_AGENT };
await switchAgentTool.invoke({ input, ... }, token); Defensive patterns
Strategy: validation
Validate before calling
const SUPPORTED_SWITCH_AGENT = 'Plan' as const;
function isValidSwitchAgentName(name: unknown): name is typeof SUPPORTED_SWITCH_AGENT {
return name === SUPPORTED_SWITCH_AGENT;
}
// before invoking:
if (!isValidSwitchAgentName(options.input.agentName)) {
return new LanguageModelToolResult([
new LanguageModelTextPart(`Cannot switch agent. Only "${SUPPORTED_SWITCH_AGENT}" is supported.`)
]);
}
await switchAgentTool.invoke(options, token); Type guard
function isSupportedAgentName(name: string): name is 'Plan' {
return name === 'Plan';
} Try / catch
try {
await switchAgentTool.invoke(options, token);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
if (msg.includes('Only "Plan" agent is supported')) {
// surface a model-friendly correction rather than crashing the turn
return new LanguageModelToolResult([new LanguageModelTextPart('Only the "Plan" agent can be selected.')]);
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Only "Plan" agent is supported. Received: "{0}"
- Missing API key for custom URL (${this.urlOrRequestMetadata}
AI-assisted analysis of microsoft/vscode@a94b963a32 (2026-08-12).
Data as JSON: /api/errors/4af6fd95b6a64b11.
Report an issue: GitHub.