{"record":{"id":"35a2721ce403bab3","repo":"can1357/oh-my-pi","slug":"reason-35a272","errorCode":null,"errorMessage":"${reason}","messagePattern":"\\$\\{reason\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/extensibility/extensions/wrapper.ts","lineNumber":227,"sourceCode":"\t\tlet effectiveParams = params;\n\t\tif (!loopEmittedToolCall && this.runner.hasHandlers(\"tool_call\")) {\n\t\t\ttry {\n\t\t\t\tconst callResult = (await this.runner.emitToolCall(\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\ttoolName: this.tool.name,\n\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\tinput: normalizeToolEventInput(\n\t\t\t\t\t\t\tthis.tool.name,\n\t\t\t\t\t\t\tresolveToolEventInput(this.tool, toolEventArgs(params, context)),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\tsignal,\n\t\t\t\t)) as ToolCallEventResult | undefined;\n\n\t\t\t\tif (callResult?.block) {\n\t\t\t\t\tconst reason = callResult.reason || \"Tool execution was blocked by an extension\";\n\t\t\t\t\tthrow new Error(reason);\n\t\t\t\t}\n\t\t\t\t// A non-blocking handler may replace the execution input. The returned object is the raw\n\t\t\t\t// input passed to `execute` (handler-owned; not re-normalized). Skipped for `computer`\n\t\t\t\t// tool calls, whose event input is a synthetic {actions,pendingSafetyChecks} view\n\t\t\t\t// (see toolEventArgs) rather than the real execution params.\n\t\t\t\tif (callResult?.input !== undefined && context?.toolCall?.providerMetadata?.type !== \"computer\") {\n\t\t\t\t\teffectiveParams = callResult.input as typeof params;\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error) {\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t\tthrow new Error(`Extension failed, blocking execution: ${String(err)}`);\n\t\t\t}\n\t\t}\n\n\t\t// 2. Full approval gate against the (possibly revised) input that will actually run — resolves\n\t\t// policy and prompts on `effectiveParams`, so the user approves exactly what executes. A revised","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/extensions/wrapper.ts#L209-L245","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait tool.execute(...); // throws 'Writes outside /tmp are not allowed'\n// after\ntry {\n  await tool.execute(...);\n} catch (err) {\n  if (err.message.includes('blocked by an extension') || isExtensionBlock(err)) {\n    logger.warn('Tool call blocked by policy', { reason: err.message });\n    return; // controlled refusal\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"const verdict = policyExtension.allows(toolName, params);\nif (!verdict.allowed) {\n  logger.warn('call would be blocked', { reason: verdict.reason });\n  return;\n}","typeGuard":"function isExtensionBlockError(err: unknown): err is Error & { blockedByExtension: true } {\n  return err instanceof Error && (err.message.includes('blocked by an extension') || typeof (err as any).blockedByExtension === 'boolean');\n}","tryCatchPattern":"try {\n  result = await execute(...);\n} catch (err) {\n  if (isExtensionBlockError(err)) {\n    logger.warn('tool call blocked by extension policy', { reason: err.message });\n    return blockedResult(err.message);\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["extensions","policy","tool-execution","blocked"],"backgroundTag":"tool-execution-blocked-by-extension","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}