can1357/oh-my-pi · error · Error

Extension failed, blocking execution: ${String(err)}

Error message

Extension failed, blocking execution: ${String(err)}

What it means

An extension hook (before-tool-call) threw a non-Error value during tool execution. The wrapper only re-throws real Error instances; anything else (strings, thrown objects, rejected non-Error promises) is wrapped in a new Error prefixed with 'Extension failed, blocking execution'. Blocking extensions fail tool execution closed by design.

Source

Thrown at packages/coding-agent/src/extensibility/extensions/wrapper.ts:240

					signal,
				)) as ToolCallEventResult | undefined;

				if (callResult?.block) {
					const reason = callResult.reason || "Tool execution was blocked by an extension";
					throw new Error(reason);
				}
				// A non-blocking handler may replace the execution input. The returned object is the raw
				// input passed to `execute` (handler-owned; not re-normalized). Skipped for `computer`
				// tool calls, whose event input is a synthetic {actions,pendingSafetyChecks} view
				// (see toolEventArgs) rather than the real execution params.
				if (callResult?.input !== undefined && context?.toolCall?.providerMetadata?.type !== "computer") {
					effectiveParams = callResult.input as typeof params;
				}
			} catch (err) {
				if (err instanceof Error) {
					throw err;
				}
				throw new Error(`Extension failed, blocking execution: ${String(err)}`);
			}
		}

		// 2. Full approval gate against the (possibly revised) input that will actually run — resolves
		// policy and prompts on `effectiveParams`, so the user approves exactly what executes. A revised
		// input that newly resolves to `deny` is caught here even though the original passed the
		// short-circuit above.
		const resolvedArgs = approvalArgs(effectiveParams, context);
		const resolved = resolveApproval(this.tool, resolvedArgs, approvalMode, userPolicies);
		context?.xdevTierResolved?.(resolved.tier);
		if (resolved.policy === "deny") {
			throw denyError(resolved, this.tool.name);
		}
		const pendingSafetyChecks = computerSafetyChecks(context);
		// An xd:// device dispatch already cleared the write tool's outer gate at
		// this tool's tier — re-prompting would double-ask for one action. The
		// bypass only holds while the input is exactly what that outer gate
		// approved: a handler revision here may have raised the tier, so revised

View on GitHub (pinned to 9690622007)

Solutions

  1. Fix the extension handler to throw/reject with Error instances (new Error(...))
  2. Inspect the stringified value after the prefix to identify which extension threw and why
  3. Temporarily disable extensions to confirm which one is blocking execution
  4. Wrap third-party extension handlers in your own try/catch that converts non-Error throws

Example fix

// before (in extension handler)
if (!params.path) throw 'path required';
// after
if (!params.path) throw new Error('path required');
Defensive patterns

Strategy: try-catch

Validate before calling

function throwsError(fn) { try { fn(); } catch (e) { return e instanceof Error; } return true; }

Type guard

function isError(e: unknown): e is Error { return e instanceof Error; }

Try / catch

try { await tool.execute(params); } catch (err) {
  if (err instanceof Error && err.message.startsWith('Extension failed, blocking execution:')) {
    logger.warn('extension blocked tool', { detail: err.message });
  } else throw err;
}

Prevention

When it happens

Trigger: An extension handler registered for a tool-call event throws a raw string (throw 'bad params'), throws a plain object, or returns a rejected promise with a non-Error reason while the tool is executing via wrapper.execute().

Common situations: Custom or third-party extension authors using throw 'message' instead of new Error('message'); handlers rejecting with response payloads or plain objects; transpilation/bundling changing thrown types.

Related errors


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