can1357/oh-my-pi · error · ToolError
\`${name}\` cannot run through the eval bridge; call the dir
Error message
\`${name}\` cannot run through the eval bridge; call the direct \`${name}\` tool. What it means
`checkpoint` and `rewind` are special-cased in `callSessionTool`: the session only honors them as direct toolResult messages, so a bridged call from JS eval would report success without actually taking effect. The bridge deliberately rejects these names with a `ToolError` instructing the caller to use the direct tool instead.
Source
Thrown at packages/coding-agent/src/eval/js/tool-bridge.ts:126
}
export async function callSessionTool(name: string, args: unknown, options: ToolBridgeOptions): Promise<ToolValue> {
if (name === EVAL_COMPLETION_BRIDGE_NAME) {
return await runEvalCompletion(args, options);
}
if (name === EVAL_AGENT_BRIDGE_NAME) {
return await runEvalAgent(args, options);
}
if (name === EVAL_BUDGET_BRIDGE_NAME) {
return await runEvalBudget(args, options);
}
if (name === EVAL_CONCURRENCY_BRIDGE_NAME) {
return runEvalConcurrency(args, options);
}
if (name === "checkpoint" || name === "rewind") {
// The session recognizes checkpoint/rewind only as direct toolResult
// messages; a bridged call would report success without taking effect.
throw new ToolError(`\`${name}\` cannot run through the eval bridge; call the direct \`${name}\` tool.`);
}
const tool = getTool(options.session, name);
const normalizedArgs = normalizeArgs(args);
const toolCallId = `js-${name}-${crypto.randomUUID()}`;
try {
const result = await tool.execute(
toolCallId,
normalizedArgs,
options.signal,
undefined,
options.session.getToolContext?.(),
);
const textBlocks = result.content.filter(
(content): content is { type: "text"; text: string } =>
content.type === "text" && typeof content.text === "string",
);
const imageBlocks = result.content.filter(
(content): content is { type: "image"; mimeType: string; data: string } =>View on GitHub (pinned to 9690622007)
Solutions
- Don't call checkpoint/rewind from eval code; end the eval run and let the agent emit the direct toolResult message
- Restructure the workflow so checkpoint/rewind is triggered by the agent's normal tool call path
- If programmatic control is needed, use the SDK/session API that supports checkpoint semantics directly rather than the eval bridge
Example fix
// before (eval code)
await tool("checkpoint", {});
// after: call checkpoint as a direct tool call in the agent loop, not via eval bridge
// or remove the call and let the agent issue the checkpoint itself Defensive patterns
Strategy: validation
Validate before calling
const BRIDGE_BLOCKED = new Set(["checkpoint", "rewind"]);
if (BRIDGE_BLOCKED.has(name)) {
throw new Error(`${name} must be issued as a direct tool call, not via the eval bridge`);
} Try / catch
try {
await tool("checkpoint", {});
} catch (err) {
if (err instanceof ToolError && /cannot run through the eval bridge/.test(err.message)) {
// defer: request the agent to issue checkpoint directly
}
} Prevention
- Never call checkpoint/rewind from eval-bridged code
- Keep state snapshot/restore logic in the agent's direct tool path
- Document which tools are bridge-restricted for eval script authors
When it happens
Trigger: Invoking `tool("checkpoint", ...)` or `tool("rewind", ...)` from within the JS eval bridge runtime.
Common situations: Eval scripts attempting to snapshot or restore session state programmatically, assuming all tools are bridged equally.
Related errors
- Unknown tool from js runtime: ${name}
- symlinks not supported on this platform
- Unsupported shell${shell ? ` "${shell}"` : ""}. Supported sh
- agent() received invalid arguments: ${result.summary}
- agent() blocked: turn token budget exhausted (${turnBudget.s
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a7b76e62a543be63.
Report an issue: GitHub.