can1357/oh-my-pi · error · ToolError

No pending action to apply — ${RESOLVE_DEVICE_PATH} is only

Error message

No pending action to apply — ${RESOLVE_DEVICE_PATH} is only valid while a staged preview is pending.${proposeHint}

What it means

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.

Source

Thrown at packages/coding-agent/src/tools/resolve.ts:335

	if (!invoker) {
		session.clearPendingInvokers?.();
		const proposeHint = session.peekPlanProposalHandler?.()
			? ` To submit the plan for approval, write its title to ${PROPOSE_DEVICE_PATH} instead.`
			: "";
		// Rejecting is a request to reach the "no staged change" end-state, which
		// already holds when nothing is pending — honor it as a successful
		// cancellation instead of surfacing a hard error. Apply still errors.
		if (action === "discard") {
			const details: ResolveDetails = { action, reason: body };
			return {
				result: {
					content: [{ type: "text", text: `Nothing to reject; no pending action remains.${proposeHint}` }],
					details,
				},
				xdev: { ...xdevBase, inner: details },
			};
		}
		throw new ToolError(
			`No pending action to apply — ${RESOLVE_DEVICE_PATH} is only valid while a staged preview is pending.${proposeHint}`,
		);
	}
	const invocation: ResolveInvocation = { action, reason: body };
	const result = (await invoker(invocation)) as AgentToolResult<ResolveDetails>;
	return { result, xdev: { ...xdevBase, inner: result.details } };
}

/** Streaming-safe call preview for a resolution-device write: `Resolve/Reject/Propose: <text>`. */
export function renderResolutionDeviceCall(device: ResolutionDeviceName, content: unknown, uiTheme: Theme): Component {
	const body = typeof content === "string" ? replaceTabs(content.trim().split("\n")[0] ?? "") : "";
	const title = device === PROPOSE_DEVICE_NAME ? "Propose" : device === REJECT_DEVICE_NAME ? "Reject" : "Resolve";
	const text = renderStatusLine(
		{
			icon: "pending",
			title,
			description: body ? truncateToWidth(body, 72, Ellipsis.Omit) : undefined,
		},

View on GitHub (pinned to 9690622007)

Solutions

  1. Only call resolve after a tool staged a pending preview
  2. Re-run the staging step (plan/preview) before applying
  3. If plan mode is active, follow the proposeHint and use the propose device instead
Defensive patterns

Strategy: validation

Validate before calling

if (!hasPendingStagedAction()) { throw new Error("Nothing staged: run the plan/preview step before resolving"); }

Try / catch

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; }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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