can1357/oh-my-pi · error

Pending action store unavailable for custom tools in this ru

Error message

Pending action store unavailable for custom tools in this runtime.

What it means

CustomToolLoader's shared API exposes pi.pushPendingAction to custom tools, but the closure only forwards to a real pending-action store when one was injected via the loader constructor's optional pushPendingAction parameter. In runtimes that do not supply one (e.g. headless/SDK embedding without UI action support), the API stub throws this error when a custom tool attempts to register a pending (confirmable) action.

Source

Thrown at packages/coding-agent/src/extensibility/custom-tools/loader.ts:156

			sourceToolName: string;
			apply(reason: string): Promise<AgentToolResult<unknown>>;
			reject?(reason: string): Promise<AgentToolResult<unknown> | undefined>;
		}) => void,
	) {
		this.#sharedApi = {
			cwd,
			exec: (command: string, args: string[], options?: ExecOptions) =>
				execCommand(command, args, options?.cwd ?? cwd, options),
			ui: createNoOpUIContext(),
			hasUI: false,
			logger,
			typebox,
			arktype: type,
			zod,
			pi,
			pushPendingAction: action => {
				if (!pushPendingAction) {
					throw new Error("Pending action store unavailable for custom tools in this runtime.");
				}
				pushPendingAction({
					label: action.label,
					sourceToolName: action.sourceToolName ?? "custom_tool",
					apply: action.apply,
					reject: action.reject,
				});
			},
		};
		this.#seenNames = new Set<string>(builtInToolNames);
	}

	async load(pathsWithSources: ToolPathWithSource[]): Promise<void> {
		for (const { path: toolPath, source } of pathsWithSources) {
			const { tools: loadedTools, errors } = await loadTool(toolPath, this.#sharedApi.cwd, this.#sharedApi, source);
			this.errors.push(...errors);

			for (const loadedTool of loadedTools) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Guard the tool: check whether pending actions are supported in the runtime before registering, or wrap the call in try/catch and degrade gracefully (auto-apply instead of asking).
  2. Run the tool in the interactive TUI runtime where the pending action store is provided to CustomToolLoader.
  3. If you embed the SDK and need pending actions, pass a pushPendingAction implementation when constructing CustomToolLoader.

Example fix

// before (inside custom tool)
pi.pushPendingAction({ label: 'Apply migration?', apply });
// after
try {
  pi.pushPendingAction({ label: 'Apply migration?', apply });
} catch {
  await apply('pending actions unsupported in this runtime'); // auto-apply fallback
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  pi.pushPendingAction({ label, apply });
} catch (err) {
  if (err instanceof Error && err.message.includes('Pending action store unavailable')) {
    await apply('pending actions unsupported; auto-applying');
  } else throw err;
}

Prevention

When it happens

Trigger: A custom tool calls pi.pushPendingAction({label, apply, ...}) while running in a runtime where CustomToolLoader was constructed without the pushPendingAction callback — e.g. RPC/SDK/non-interactive contexts where pending UI actions aren't wired.

Common situations: Writing a custom tool that uses pending actions and testing it in an SDK/embedded runtime; running the agent headless where the interactive UI action store doesn't exist; a tool written for the TUI being reused in a background job.

Related errors


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