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

  1. Verify the tool name matches a currently registered native built-in (check getAllTools()/registry).
  2. Ensure the runner is constructed with a nativeToolResolver (host wiring) before invokeTool is used.
  3. 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

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


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