can1357/oh-my-pi · warning · Error
${reason}
Error message
${reason} What it means
During tool execution, extension call-handlers may veto the call by returning { block: true, reason }. When that happens, wrapper.execute throws an Error whose message is the handler's reason (defaulting to 'Tool execution was blocked by an extension'). The error originates from your own extension's blocking logic, not a core failure.
Source
Thrown at packages/coding-agent/src/extensibility/extensions/wrapper.ts:227
let effectiveParams = params;
if (!loopEmittedToolCall && this.runner.hasHandlers("tool_call")) {
try {
const callResult = (await this.runner.emitToolCall(
{
type: "tool_call",
toolName: this.tool.name,
toolCallId,
input: normalizeToolEventInput(
this.tool.name,
resolveToolEventInput(this.tool, toolEventArgs(params, context)),
),
},
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 revisedView on GitHub (pinned to 9690622007)
Solutions
- Read the error message reason to identify which extension blocked the call and why.
- Adjust or disable the blocking extension's policy/config so the intended call is permitted.
- Run with extensions disabled or a reduced set to confirm which extension is the blocker, then fix its rules.
- If the block is intentional, handle it in your code as a controlled refusal instead of an unexpected failure.
Example fix
// before
await tool.execute(...); // throws 'Writes outside /tmp are not allowed'
// after
try {
await tool.execute(...);
} catch (err) {
if (err.message.includes('blocked by an extension') || isExtensionBlock(err)) {
logger.warn('Tool call blocked by policy', { reason: err.message });
return; // controlled refusal
}
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
const verdict = policyExtension.allows(toolName, params);
if (!verdict.allowed) {
logger.warn('call would be blocked', { reason: verdict.reason });
return;
} Type guard
function isExtensionBlockError(err: unknown): err is Error & { blockedByExtension: true } {
return err instanceof Error && (err.message.includes('blocked by an extension') || typeof (err as any).blockedByExtension === 'boolean');
} Try / catch
try {
result = await execute(...);
} catch (err) {
if (isExtensionBlockError(err)) {
logger.warn('tool call blocked by extension policy', { reason: err.message });
return blockedResult(err.message);
}
throw err;
} Prevention
- Log the error message — it is the blocking extension's own reason and names the policy.
- Keep a manifest of installed policy extensions and review their rules when blocks surprise you.
- Test CI/dev with the same extension set to catch environment-specific blocks.
- Provide allowlists/config in policy extensions so users can unblock intended calls without disabling them.
When it happens
Trigger: An extension registered a tool-call handler that returns { block: true } for the given tool/params — e.g. a safety policy, allowlist, or audit rule matched this call.
Common situations: A security/policy extension blocks writes outside an allowed directory; a rate-limit extension blocks after N calls; a custom guard blocks 'rm' commands; the block fires in CI where config differs from local dev.
Related errors
- Tool execution was blocked by a hook
- Pending action store unavailable for custom tools in this ru
- Extension runtime not initialized. Action methods cannot be
- Composer shape id must be a non-empty trimmed string
- Composer shape "${id}" must have a label
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/35a2721ce403bab3.
Report an issue: GitHub.