can1357/oh-my-pi · error · ToolError

Plan mode is not active.

Error message

Plan mode is not active.

What it means

#handleAcpPlanProposal implements the plan-proposal tool used while ACP plan mode is active. It first checks session.getPlanModeState()?.enabled; if plan mode is not enabled it throws this ToolError. This guards the workflow contract: plan proposals are only meaningful (and only get auto-approval routing) when the session is actually in plan mode.

Source

Thrown at packages/coding-agent/src/modes/acp/acp-agent.ts:1884

	}

	/**
	 * Plan-proposal handler installed while ACP plan mode is active. The agent
	 * submits the finalized plan by writing its `<slug>`/title to
	 * `xd://propose`; this handler validates the plan file, normalizes the
	 * title, asks the ACP client to confirm (via `unstable_createElicitation`
	 * when supported), and on approval keeps the chosen plan path, exits plan
	 * mode, and notifies the client so the agent regains full tools.
	 *
	 * Mirrors `InteractiveMode.#handlePlanProposal` for the parts the agent sees
	 * (same `PlanApprovalDetails` shape). Clients without form-mode elicitation
	 * get an auto-approve so plan mode is never stranded — the agent always has
	 * a way out.
	 */
	async #handleAcpPlanProposal(session: AgentSession, title: string): Promise<AgentToolResult<unknown>> {
		const state = session.getPlanModeState();
		if (!state?.enabled) {
			throw new ToolError("Plan mode is not active.");
		}
		const {
			planFilePath,
			planContent,
			title: resolvedTitle,
		} = await resolveApprovedPlan({
			suppliedTitle: title,
			statePlanFilePath: state.planFilePath,
			readPlan: url => this.#readAcpPlanFile(session, url),
			listPlanFiles: () => this.#listAcpLocalPlanFiles(session),
		});
		const approved = await this.#requestAcpPlanApprovalChoice(session.sessionId, resolvedTitle, planContent);
		const details: PlanApprovalDetails = {
			planFilePath,
			title: resolvedTitle,
			planExists: true,
		};
		if (!approved) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Enable plan mode on the session (setPlanModeState({ enabled: true, ... }) or an ACP mode change to ACP_PLAN_MODE_ID) before proposing a plan.
  2. Verify the session instance is the same one whose plan mode was enabled.
  3. Have the model/agent flow check plan-mode state before invoking the plan-proposal tool.

Example fix

// before
await proposePlan(session, title);
// after
session.setPlanModeState({ enabled: true, planFilePath: DEFAULT_PLAN_FILE_URL });
await proposePlan(session, title);
Defensive patterns

Strategy: try-catch

Validate before calling

const state = session.getPlanModeState();
if (!state?.enabled) {
  session.setPlanModeState({ enabled: true, planFilePath: DEFAULT_PLAN_FILE_URL });
}
await proposePlan(session, title);

Type guard

function planModeActive(session: AgentSession): boolean {
  return session.getPlanModeState()?.enabled === true;
}

Try / catch

try {
  await proposePlan(session, title);
} catch (err) {
  if (err instanceof ToolError && err.message === "Plan mode is not active.") {
    session.setPlanModeState({ enabled: true, planFilePath: DEFAULT_PLAN_FILE_URL });
    await proposePlan(session, title);
  } else throw err;
}

Prevention

When it happens

Trigger: Invoking the plan-proposal tool (or the agent reaching #handleAcpPlanProposal) while session.getPlanModeState() is null or { enabled: false } — e.g. the model calls the plan tool after plan mode was exited or never entered.

Common situations: A session was switched out of plan mode mid-run but the model still attempts the plan tool; a caller resumes a session whose plan-mode state was not persisted; the ACP client enabled plan mode on a different session object than the one running.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/32079a1be089c4a4. Report an issue: GitHub.