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

  1. Ensure the tool is only called with agentName exactly equal to 'Plan' (case-sensitive) — this is the only accepted value.
  2. If you are driving the tool from a test or simulation, pass { agentName: 'Plan' } as options.input.
  3. 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.
  4. 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

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


AI-assisted analysis of microsoft/vscode@a94b963a32 (2026-08-12). Data as JSON: /api/errors/4af6fd95b6a64b11. Report an issue: GitHub.