{"record":{"id":"1e89c83be126719d","repo":"can1357/oh-my-pi","slug":"no-plan-is-awaiting-approval-propose-device-pa","errorCode":null,"errorMessage":"No plan is awaiting approval — ${PROPOSE_DEVICE_PATH} only accepts a plan title while plan mode is active.","messagePattern":"No plan is awaiting approval — (.+?) only accepts a plan title while plan mode is active\\.","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/resolve.ts","lineNumber":306,"sourceCode":"}\n\n/**\n * Execute a resolution-device write. `text` is the raw plain-text body:\n * - `xd://resolve` / `xd://reject` → the reason; dispatches to the pending\n *   preview invoker (in-flight queue directive first).\n * - `xd://propose` → the plan title; dispatches to the plan-proposal handler\n *   installed by plan mode.\n */\nexport async function dispatchResolutionDevice(\n\tsession: ToolSession,\n\tdevice: ResolutionDeviceName,\n\ttext: string,\n): Promise<{ result: AgentToolResult<unknown>; xdev: XdevDispatch }> {\n\tconst body = text.trim();\n\tif (device === PROPOSE_DEVICE_NAME) {\n\t\tconst handler = session.peekPlanProposalHandler?.();\n\t\tif (!handler) {\n\t\t\tthrow new ToolError(\n\t\t\t\t`No plan is awaiting approval — ${PROPOSE_DEVICE_PATH} only accepts a plan title while plan mode is active.`,\n\t\t\t);\n\t\t}\n\t\tconst result = await handler(body);\n\t\treturn { result, xdev: { tool: device, mode: \"execute\", args: { title: body }, inner: result.details } };\n\t}\n\n\tconst action: ResolveAction = device === RESOLVE_DEVICE_NAME ? \"apply\" : \"discard\";\n\tconst xdevBase: XdevDispatch = { tool: device, mode: \"execute\", args: { reason: body } };\n\tconst invoker = session.peekQueueInvoker?.() ?? session.peekPendingInvoker?.();\n\tif (!invoker) {\n\t\tsession.clearPendingInvokers?.();\n\t\tconst proposeHint = session.peekPlanProposalHandler?.()\n\t\t\t? ` To submit the plan for approval, write its title to ${PROPOSE_DEVICE_PATH} instead.`\n\t\t\t: \"\";\n\t\t// Rejecting is a request to reach the \"no staged change\" end-state, which\n\t\t// already holds when nothing is pending — honor it as a successful\n\t\t// cancellation instead of surfacing a hard error. Apply still errors.","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/resolve.ts#L288-L324","documentation":"dispatchResolutionDevice handles the 'xdev propose' device. The propose path only accepts a plan title while a plan proposal handler is registered (plan mode active). If session.peekPlanProposalHandler() returns undefined — no plan is awaiting approval — it throws this ToolError pointing at the PROPOSE_DEVICE_PATH usage contract.","triggerScenarios":"Invoking the propose device when plan mode is not active, the plan was already approved/rejected, or the session never registered a plan proposal handler.","commonSituations":"Model tries to propose a plan title outside plan mode; a second propose after one was handled; stale transcript replayed into a new session without plan mode.","solutions":["Enter plan mode / create a plan proposal before using the propose device","Use the resolve device (RESOLVE_DEVICE_PATH) to approve/reject an existing pending plan instead","Check the session state: only one plan can await approval at a time"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!session.peekPlanProposalHandler?.()) { throw new Error(\"Plan mode is not active; cannot use the propose device\"); }","typeGuard":"function canPropose(session: ToolSession): boolean { return typeof session.peekPlanProposalHandler?.() === \"function\"; }","tryCatchPattern":"try { await proposePlan(title); } catch (e) { if (e instanceof ToolError && e.message.includes(\"No plan is awaiting approval\")) { /* fall back to resolve device or enable plan mode */ } else throw e; }","preventionTips":["Only invoke propose while plan mode is active with a pending proposal","Track whether a proposal was already approved/rejected this session","Prefer the resolve device when a plan already exists"],"tags":["plan-mode","state","tool-error"],"backgroundTag":"invalid-tool-state","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}