can1357/oh-my-pi · error
Unsupported ACP mode: ${modeId}
Error message
Unsupported ACP mode: ${modeId} What it means
AcpAgent.#applyModeChange validates the requested modeId against the modes advertised by #getAvailableModes for the session (currently default vs plan). Requesting any other id throws this error instead of silently ignoring the switch. This guarantees the client's mode selector never desyncs from what the session actually supports.
Source
Thrown at packages/coding-agent/src/modes/acp/acp-agent.ts:1847
if (session.settings.get("plan.enabled")) {
modes.push({
id: ACP_PLAN_MODE_ID,
name: "Plan",
description: "Read-only planning mode that drafts a plan to a markdown file before any code changes",
});
}
void session;
return modes;
}
#getCurrentModeId(session: AgentSession): string {
return session.getPlanModeState()?.enabled ? ACP_PLAN_MODE_ID : ACP_DEFAULT_MODE_ID;
}
#applyModeChange(session: AgentSession, modeId: string): void {
const availableModes = this.#getAvailableModes(session);
if (!availableModes.some(mode => mode.id === modeId)) {
throw new Error(`Unsupported ACP mode: ${modeId}`);
}
if (modeId === ACP_PLAN_MODE_ID) {
const previous = session.getPlanModeState();
session.setPlanModeState({
enabled: true,
planFilePath: previous?.planFilePath ?? DEFAULT_PLAN_FILE_URL,
workflow: previous?.workflow ?? "parallel",
reentry: previous !== undefined,
});
// Mirror `InteractiveMode.#enterPlanMode`: register the plan-proposal
// handler that consumes `xd://propose` writes from plan mode. Without
// this, proposal dispatch falls through and plan mode has no approval
// path (issue #1869).
session.setPlanProposalHandler?.(title => this.#handleAcpPlanProposal(session, title));
} else {
session.setPlanProposalHandler?.(null);
session.setPlanModeState(undefined);
}View on GitHub (pinned to 9690622007)
Solutions
- List the session's current modes (via the ACP modes/capabilities surface) and send only an id that appears there.
- Refresh the client's cached mode ids after (re)connecting so it matches ACP_DEFAULT_MODE_ID / ACP_PLAN_MODE_ID.
- If a new mode is needed, extend #getAvailableModes in acp-agent.ts rather than sending a custom id.
Example fix
// before
await session.setMode("code");
// after
const modes = await session.availableModes();
await session.setMode(modes.find(m => m.id === ACP_DEFAULT_MODE_ID).id); Defensive patterns
Strategy: validation
Validate before calling
const modes = await getAvailableModes(session);
if (!modes.some(m => m.id === modeId)) throw new Error(`Mode ${modeId} not offered by session`);
await session.setMode(modeId); Try / catch
try {
await session.setMode(modeId);
} catch (err) {
if (err.message.startsWith("Unsupported ACP mode")) {
await session.setMode(ACP_DEFAULT_MODE_ID);
} else throw err;
} Prevention
- Always fetch the session's advertised modes before switching; never cache ids across sessions.
- Treat mode ids as opaque strings from the capabilities response.
- Refresh mode lists after reconnects or session restores.
When it happens
Trigger: Sending an ACP set-mode / mode-change request whose modeId is not in the session's availableModes list — e.g. a stale or client-invented mode id, or plan-mode id on a session where plan mode is not exposed.
Common situations: An editor extension caches mode ids from a previous session; a client hardcodes a mode id from a different tool's schema; the session's plan-mode support changed but the client kept sending the old plan mode id.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- ACP cwd must be absolute: ${cwd}
- Invalid ACP session cursor: ${cursor}
- Invalid CPIO archive: mode exceeds 16 bits
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/be9e661b2c610c6a.
Report an issue: GitHub.