{"record":{"id":"0202ec625c5aa0bc","repo":"can1357/oh-my-pi","slug":"no-pending-action-to-apply-resolve-device-path","errorCode":null,"errorMessage":"No pending action to apply — ${RESOLVE_DEVICE_PATH} is only valid while a staged preview is pending.${proposeHint}","messagePattern":"No pending action to apply — (.+?) is only valid while a staged preview is pending\\.(.+?)","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/resolve.ts","lineNumber":335,"sourceCode":"\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.\n\t\tif (action === \"discard\") {\n\t\t\tconst details: ResolveDetails = { action, reason: body };\n\t\t\treturn {\n\t\t\t\tresult: {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Nothing to reject; no pending action remains.${proposeHint}` }],\n\t\t\t\t\tdetails,\n\t\t\t\t},\n\t\t\t\txdev: { ...xdevBase, inner: details },\n\t\t\t};\n\t\t}\n\t\tthrow new ToolError(\n\t\t\t`No pending action to apply — ${RESOLVE_DEVICE_PATH} is only valid while a staged preview is pending.${proposeHint}`,\n\t\t);\n\t}\n\tconst invocation: ResolveInvocation = { action, reason: body };\n\tconst result = (await invoker(invocation)) as AgentToolResult<ResolveDetails>;\n\treturn { result, xdev: { ...xdevBase, inner: result.details } };\n}\n\n/** Streaming-safe call preview for a resolution-device write: `Resolve/Reject/Propose: <text>`. */\nexport function renderResolutionDeviceCall(device: ResolutionDeviceName, content: unknown, uiTheme: Theme): Component {\n\tconst body = typeof content === \"string\" ? replaceTabs(content.trim().split(\"\\n\")[0] ?? \"\") : \"\";\n\tconst title = device === PROPOSE_DEVICE_NAME ? \"Propose\" : device === REJECT_DEVICE_NAME ? \"Reject\" : \"Resolve\";\n\tconst text = renderStatusLine(\n\t\t{\n\t\t\ticon: \"pending\",\n\t\t\ttitle,\n\t\t\tdescription: body ? truncateToWidth(body, 72, Ellipsis.Omit) : undefined,\n\t\t},","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/resolve.ts#L317-L353","documentation":"dispatchResolutionDevice handles the resolve device (apply/reject a staged preview). If action is apply/reject but there is no pending action — nothing was staged, or it was already applied/rejected — and there is nothing left to reject, it throws this ToolError; proposeHint suggests the propose device when plan mode is active.","triggerScenarios":"Calling resolve with action=apply when no preview is pending; calling it twice (second call after the first consumed the staged action); reject when no action remains (the reject branch returns a friendly text for empty-pending, but apply throws).","commonSituations":"Double-apply race from an agent loop retry; session restarted losing the staged state; model invokes resolve before running a plan that stages an action.","solutions":["Only call resolve after a tool staged a pending preview","Re-run the staging step (plan/preview) before applying","If plan mode is active, follow the proposeHint and use the propose device instead"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!hasPendingStagedAction()) { throw new Error(\"Nothing staged: run the plan/preview step before resolving\"); }","typeGuard":null,"tryCatchPattern":"try { await resolveApply(); } catch (e) { if (e instanceof ToolError && e.message.includes(\"No pending action to apply\")) { /* re-stage the action instead of retrying blindly */ } else throw e; }","preventionTips":["Call resolve exactly once per staged preview; guard against retry loops double-applying","Check pending-state details after each resolve call","Re-run staging after any session restart"],"tags":["state","pending-action","tool-error"],"backgroundTag":"invalid-tool-state","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}