vercel/ai · error · RunError

CODE_MODE_HOST_TOOL_ERROR

CODE_MODE_HOST_TOOL_ERROR

Error message

Host tool failed.

What it means

When a host tool invoked from code-mode sandbox code throws an error that is neither a CodeModeError, a RunError, nor the internal HostFunctionInterruptSignal, invokeCodeModeTool replaces it with a generic RunError('Host tool failed.', 'CODE_MODE_HOST_TOOL_ERROR'). This avoids leaking arbitrary tool implementation errors (which may contain sensitive details) into the sandbox, at the cost of hiding the original message.

Source

Thrown at packages/code-mode/src/run-code-mode.ts:341

      ...(codeModeInterrupt === undefined ? {} : { codeModeInterrupt }),
      skipApproval,
    });
    if (outcome.type === 'interrupted') {
      return context.interrupt(outcome.payload);
    }
    return fromJsonPayload(outcome.valueJson);
  } catch (error) {
    if (error instanceof CodeModeError) {
      codeModeErrors.push(error);
      throw new RunError(error.message, error.code, error.details);
    }
    if (
      RunError.isInstance(error) ||
      (error instanceof Error && error.name === 'HostFunctionInterruptSignal')
    ) {
      throw error;
    }
    throw new RunError('Host tool failed.', 'CODE_MODE_HOST_TOOL_ERROR');
  }
}

function assertInterruptPayload(value: unknown): CodeModeInterruptPayload {
  if (
    typeof value !== 'object' ||
    value === null ||
    Array.isArray(value) ||
    typeof (value as { kind?: unknown }).kind !== 'string'
  ) {
    throw new CodeModeProtocolError(
      'Code mode interruption payload is malformed.',
    );
  }
  return value as CodeModeInterruptPayload;
}

function toRunLimits(

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Check the tool's own logs/server-side for the underlying exception — the original message is intentionally not forwarded to the sandbox.
  2. Make the tool catch its own errors and return a structured error value (or throw a CodeModeError with details) instead of a raw Error.
  3. Add retry/error handling inside the tool execute function for transient failures like network errors.

Example fix

// before
execute: async input => { const res = await fetch(url); return await res.json(); } // raw throw -> 'Host tool failed.'
// after
execute: async input => {
  try {
    const res = await fetch(url);
    return await res.json();
  } catch (e) {
    return { error: 'fetch_failed', detail: String(e) };
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Harden tools so they never throw raw errors:
const safeTools = Object.fromEntries(Object.entries(tools).map(([name, tool]) => [name, wrapTool(tool)]));
// wrapTool catches errors and returns { error: string } instead of throwing.

Try / catch

try {
  await runCodeMode({ js, tools });
} catch (error) {
  if (RunError.isInstance(error) && error.code === 'CODE_MODE_HOST_TOOL_ERROR') {
    // check the tool's own logs for the real cause
  }
  throw error;
}

Prevention

When it happens

Trigger: A tool's execute function throws a plain Error (network failure, unhandled exception, assertion) during a code-mode run; the error does not carry a code-mode error class so it is anonymized.

Common situations: Tools calling external HTTP APIs that go down; tool implementations with bugs that throw raw Errors; SDK version drift where a tool throws a new error type not recognized by the bridge.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/851c245d06d4f9dd. Report an issue: GitHub.