{"record":{"id":"c7b0e34bf79394fe","repo":"JuliusBrussee/caveman","slug":"cave-sandbox-definition-mismatch","errorCode":"cave_sandbox_definition_mismatch","errorMessage":"cave_sandbox_definition_mismatch","messagePattern":"cave_sandbox_definition_mismatch","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/tool-worker.ts","lineNumber":109,"sourceCode":"      request.agentPath.some((item) => typeof item !== \"string\" ||\n        !/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(item)) ||\n      typeof request.rootDefinitionSha256 !== \"string\" ||\n      !/^[a-f0-9]{64}$/.test(request.rootDefinitionSha256) ||\n      typeof request.toolDefinitionSha256 !== \"string\" ||\n      !/^[a-f0-9]{64}$/.test(request.toolDefinitionSha256) ||\n      typeof request.tool !== \"string\" ||\n      !/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(request.tool) ||\n      typeof request.allowSideEffects !== \"boolean\" ||\n      typeof request.allowNetwork !== \"boolean\") {\n    throw new Error(\"cave_sandbox_request_invalid\");\n  }\n  if (request.allowNetwork !== true) installNetworkDeny();\n  const imported = await import(request.entry) as { default?: AgentDefinition; agent?: AgentDefinition };\n  let definition = imported.default ?? imported.agent;\n  if (!definition || definition.kind !== \"agent\") throw new Error(\"cave_sandbox_agent_export_missing\");\n  validateAgentGraph(definition);\n  if (agentDefinitionSHA256(definition) !== request.rootDefinitionSha256) {\n    throw new Error(\"cave_sandbox_definition_mismatch\");\n  }\n  const visited = new Set<AgentDefinition>([definition]);\n  for (const name of request.agentPath) {\n    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\");","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/tool-worker.ts#L91-L127","documentation":"The worker recomputes the sha256 digest of the imported root agent definition and compares it with request.rootDefinitionSha256. A mismatch throws `cave_sandbox_definition_mismatch`. This pins the executed definition to exactly the bytes the parent validated — if the entry module resolves to different definition content at runtime, execution is refused.","triggerScenarios":"The entry module's definition differs from what the parent hashed: the file changed on disk between hashing and spawn (hot reload/edit), the entry resolves to a different file (path alias, duplicate package install), or the definition is built nondeterministically (timestamps, random ids, env-dependent fields).","commonSituations":"Editing agent source while a dev run is in flight; monorepo resolution picking a different copy of the definition module than the one hashed; definition construction embedding `Date.now()`, random ids, or reading mutable env/config at module load.","solutions":["Restart or re-run the build so the parent re-hashes the current definition, then retry.","Make the definition deterministic: no timestamps, random values, or environment reads at module scope.","Ensure the entry path resolves to the same physical file the parent hashed (avoid path aliases/duplicated packages; check pnpm/npm layout).","Avoid mutating the definition object after the build hashed it."],"exampleFix":"// before\nexport default agent(`a-${Date.now()}`).tool(search).build();\n\n// after\nexport default agent(\"a\").tool(search).build();","handlingStrategy":"validation","validationCode":"// Before spawning, verify the on-disk definition still matches the hashed build\nconst current = agentDefinitionSHA256(importedDefinition);\nif (current !== expectedRootDefinitionSha256) {\n  throw new Error(\"definition drifted since build; re-run caveman build before invoking\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await runSandboxed(request);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"cave_sandbox_definition_mismatch\")) {\n    console.error(\"definition changed under a locked build — restart dev / re-run build\");\n  }\n  throw err;\n}","preventionTips":["Keep agent definition construction deterministic: no Date.now(), Math.random(), or env reads at module scope.","Do not edit agent source files while a locked run is in flight; restart dev after edits.","Avoid path aliases and duplicated package copies so the entry resolves to the hashed file.","Never mutate a definition object after the build computed its digest."],"tags":["sandbox","integrity","hash","drift"],"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"}