{"record":{"id":"786815d9f5d2e6c0","repo":"JuliusBrussee/caveman","slug":"cave-sandbox-tool-definition-mismatch","errorCode":"cave_sandbox_tool_definition_mismatch","errorMessage":"cave_sandbox_tool_definition_mismatch","messagePattern":"cave_sandbox_tool_definition_mismatch","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/tool-worker.ts","lineNumber":131,"sourceCode":"    const delegated = definition.tools.filter((item) =>\n      item.name === name && item.runtime?.kind === \"subagent\"\n    );\n    if (delegated.length !== 1) throw new Error(\"cave_sandbox_unknown_subagent\");\n    const child = delegated[0]!.runtime!.definition as AgentDefinition;\n    if (!child || child.kind !== \"agent\") {\n      throw new Error(\"cave_sandbox_subagent_definition_invalid\");\n    }\n    if (visited.has(child)) throw new Error(\"cave_sandbox_subagent_cycle\");\n    visited.add(child);\n    definition = child;\n  }\n  const selectedTools = definition.tools.filter((item) => item.name === request.tool);\n  if (selectedTools.length !== 1 || selectedTools[0]!.runtime?.kind === \"subagent\") {\n    throw new Error(\"cave_sandbox_unknown_tool\");\n  }\n  const selected = selectedTools[0]!;\n  if (toolDefinitionSHA256(selected) !== request.toolDefinitionSha256) {\n    throw new Error(\"cave_sandbox_tool_definition_mismatch\");\n  }\n  if (selected.effect !== \"read\" && request.allowSideEffects !== true) {\n    throw new Error(\"cave_sandbox_side_effect_denied\");\n  }\n  const value = await selected.execute(request.params as never, AbortSignal.timeout(selected.timeoutMs));\n  writeResult({ ok: true, value });\n} catch (error) {\n  writeResult({ ok: false, code: failureCode(error) });\n}\n","sourceCodeStart":113,"sourceCodeEnd":141,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/tool-worker.ts#L113-L141","documentation":"The sandbox tool worker resolves the requested tool from the agent definition, computes a SHA-256 fingerprint of the tool's full definition, and compares it to the SHA the caller (the host/parent process) supplied in request.toolDefinitionSha256. A mismatch means the caller's snapshot of the tool definition is stale or was produced from a different definition than the one loaded in this worker, so the worker refuses to execute it to prevent running an unvetted version of the tool.","triggerScenarios":"Calling runToolRequest/requestTool where request.tool resolves to exactly one tool (not a subagent) but toolDefinitionSHA256(selected) !== request.toolDefinitionSha256 — i.e. the definition was edited/regenerated after the request's hash was computed, or the request was built against a different agent definition.","commonSituations":"Editing a tool's description, parameters schema, or execute wiring while old in-flight/subagent requests still carry the old hash; caching tool definitions across deploys; hot-reloading the agent definition while a sandbox request is queued; building the request manually with a hand-copied or wrong hash.","solutions":["Regenerate the request (re-derive request.toolDefinitionSha256) from the same definition object/version currently loaded in the worker","Ensure the parent and worker load the identical agent definition file/version; pin or hash the definition once and share it","Clear stale caches of tool definitions (deploy artifacts, hot-reload leftovers) and retry","If you construct requests by hand, compute the hash with the same toolDefinitionSHA256 helper instead of a hardcoded value"],"exampleFix":"// before\nconst request = { tool: 'search', params, toolDefinitionSha256: OLD_HASH };\n// after\nconst tool = definition.tools.find((t) => t.name === 'search');\nconst request = { tool: tool.name, params, toolDefinitionSha256: toolDefinitionSHA256(tool) };","handlingStrategy":"validation","validationCode":"const tool = definition.tools.find((t) => t.name === request.tool);\nif (!tool || tool.runtime?.kind === 'subagent') throw new Error('unknown tool');\nif (toolDefinitionSHA256(tool) !== request.toolDefinitionSha256) {\n  throw new Error('stale tool definition hash — rebuild the request from the current definition');\n}","typeGuard":"function isFreshToolDefinition(tool, expectedSha) {\n  return typeof tool === 'object' && tool !== null && toolDefinitionSHA256(tool) === expectedSha;\n}","tryCatchPattern":"try {\n  const result = await runSandboxTool(request);\n} catch (error) {\n  if (error.message === 'cave_sandbox_tool_definition_mismatch') {\n    request = rebuildRequestFromCurrentDefinition(request.tool, request.params);\n    // retry once with the fresh hash\n  }\n}","preventionTips":["Always compute toolDefinitionSha256 with the same toolDefinitionSHA256 helper the worker uses","Never cache tool definition hashes across definition edits or deploys","Rebuild requests after any hot-reload of the agent definition","Pin the agent definition version so parent and worker agree"],"tags":["sandbox","integrity","hash-mismatch","tools"],"backgroundTag":"checksum-mismatch","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}