can1357/oh-my-pi · error · Error
invokeTool: no native built-in named "${name}" to delegate t
Error message
invokeTool: no native built-in named "${name}" to delegate to What it means
ExtensionRunner.invokeTool delegates to a native built-in tool looked up via the #nativeToolResolver callback. When the resolver returns nothing (no resolver installed or no built-in registered under that name), invokeTool throws this Error instead of executing.
Source
Thrown at packages/coding-agent/src/extensibility/extensions/runner.ts:584
*/
async invokeNativeTool<TDetails = unknown>(
name: string,
params: Record<string, unknown>,
options?: {
signal?: AbortSignal;
onUpdate?: AgentToolUpdateCallback<TDetails>;
depth?: number;
/**
* The caller tool's own context. Reused for the native call so metadata the native tool
* reads — `toolCall` (write/edit LSP batch flushing) and provider metadata /
* `providerSafetyApproved` (computer) — is preserved. Falls back to a fresh session tool
* context only when the caller had none.
*/
callerContext?: AgentToolContext;
},
): Promise<AgentToolResult<TDetails>> {
const resolved = this.#nativeToolResolver?.(name);
if (!resolved) throw new Error(`invokeTool: no native built-in named "${name}" to delegate to`);
const depth = options?.depth ?? 0;
if (depth >= 8) {
throw new Error(`invokeTool: delegation depth exceeded 8 (recursive invokeTool for "${name}"?)`);
}
const toolCallId = `invoke-${name}-${Date.now().toString(36)}-${depth}`;
return (await resolved.tool.execute(
toolCallId,
params as never,
options?.signal,
options?.onUpdate as never,
options?.callerContext ?? resolved.makeContext(),
)) as AgentToolResult<TDetails>;
}
constructor(
private readonly extensions: Extension[],
private readonly runtime: ExtensionRuntime,
/** Ignored: `cwd` is always read live via the `cwd` getter below, not cached here. */View on GitHub (pinned to 9690622007)
Solutions
- Verify the tool name matches a currently registered native built-in (check getAllTools()/registry).
- Ensure the runner is constructed with a nativeToolResolver (host wiring) before invokeTool is used.
- Guard the call: check resolvability or feature availability first, and surface a clear message for unknown tools.
Example fix
// before
await runner.invokeTool('bash_tool', params); // wrong name
// after
const known = runner.getAllTools().some(t => t.name === 'bash');
if (!known) throw new Error('bash tool unavailable in this host');
await runner.invokeTool('bash', params); Defensive patterns
Strategy: validation
Validate before calling
const name = 'bash';
if (!runner.getAllTools().some(t => t.name === name)) {
throw new Error(`tool '${name}' not available in this host`);
}
await runner.invokeTool(name, params); Type guard
function canInvoke(runner: ExtensionRunner, name: string): boolean {
return typeof runner.#resolveNative === 'function'; // or check a public resolvability API
} Try / catch
try {
return await runner.invokeTool(name, params);
} catch (err) {
if (err.message.startsWith('invokeTool: no native built-in')) {
throw new Error(`tool '${name}' is not registered in this host; check name and host wiring`);
}
throw err;
} Prevention
- Keep tool names in a shared constants file; never inline strings.
- Confirm the host attaches a nativeToolResolver before extensions use invokeTool.
- Check getAllTools() or a registry before invoking dynamically chosen names.
- Verify tool names after upgrading the CLI (names can be renamed).
When it happens
Trigger: An extension calls runner.invokeTool('bash', ...) but the host never wired a nativeToolResolver, or the tool name is misspelled/not a registered built-in at invoke time.
Common situations: Extension invokes a tool by a name that was renamed in a newer version; invoking from a host context (test/SDK) where native tools were not attached; typo in the tool name string.
Related errors
- invokeTool: delegation depth exceeded 8 (recursive invokeToo
- 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/67472c3a1ded73bd.
Report an issue: GitHub.