JuliusBrussee/caveman · error

cave_sandbox_tool_definition_mismatch

cave_sandbox_tool_definition_mismatch

Error message

cave_sandbox_tool_definition_mismatch

What it means

The worker hashes the selected tool definition with toolDefinitionSHA256 and compares against request.toolDefinitionSha256 supplied by the parent. A mismatch means the tool definition the worker resolved is not the exact one the parent vetted — the same fail-closed drift check as the root digest, scoped to one tool.

Source

Thrown at packages/agent/src/tool-worker.ts:131

    const delegated = definition.tools.filter((item) =>
      item.name === name && item.runtime?.kind === "subagent"
    );
    if (delegated.length !== 1) throw new Error("cave_sandbox_unknown_subagent");
    const child = delegated[0]!.runtime!.definition as AgentDefinition;
    if (!child || child.kind !== "agent") {
      throw new Error("cave_sandbox_subagent_definition_invalid");
    }
    if (visited.has(child)) throw new Error("cave_sandbox_subagent_cycle");
    visited.add(child);
    definition = child;
  }
  const selectedTools = definition.tools.filter((item) => item.name === request.tool);
  if (selectedTools.length !== 1 || selectedTools[0]!.runtime?.kind === "subagent") {
    throw new Error("cave_sandbox_unknown_tool");
  }
  const selected = selectedTools[0]!;
  if (toolDefinitionSHA256(selected) !== request.toolDefinitionSha256) {
    throw new Error("cave_sandbox_tool_definition_mismatch");
  }
  if (selected.effect !== "read" && request.allowSideEffects !== true) {
    throw new Error("cave_sandbox_side_effect_denied");
  }
  const value = await selected.execute(request.params as never, AbortSignal.timeout(selected.timeoutMs));
  writeResult({ ok: true, value });
} catch (error) {
  writeResult({ ok: false, code: failureCode(error) });
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Make tool definitions deterministic (stable schemas, timeouts, effect strings; no timestamps/random ids).
  2. Compute both rootDefinitionSha256 and toolDefinitionSha256 from the same immutable staged snapshot the worker imports.
  3. Restart dev sessions after editing tool definitions so hashes and imports agree.

Example fix

// before
tool({ name: "fetch", timeoutMs: Date.now() % 60000, ... }) // unstable hash input

// after
tool({ name: "fetch", timeoutMs: 30_000, ... }) // stable hash input
Defensive patterns

Strategy: validation

Validate before calling

import { toolDefinitionSHA256 } from "@caveman-ai/agent/build.js";
const selected = def.tools.find((t) => t.name === request.tool)!;
if (toolDefinitionSHA256(selected) !== request.toolDefinitionSha256) throw new Error("tool digest drift; re-derive before spawn");

Prevention

When it happens

Trigger: Tool definition (name, schema, effect, timeoutMs, execute identity inputs) changed between the parent's hash computation and the worker's import; nondeterministic fields inside the tool definition; parent hashed a different tool than the one name resolution found.

Common situations: Hot reload or source-graph restage between parent inspection and worker spawn; tools built with closures capturing changing config but hashed on unstable metadata; version skew between parent and sandbox copy.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/786815d9f5d2e6c0. Report an issue: GitHub.