{"record":{"id":"568d06b73fad38a5","repo":"JuliusBrussee/caveman","slug":"cave-tool-sandbox-entry-required","errorCode":"cave_tool_sandbox_entry_required","errorMessage":"cave_tool_sandbox_entry_required: sandboxed tools need entryPath","messagePattern":"cave_tool_sandbox_entry_required: sandboxed tools need entryPath","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":886,"sourceCode":"\n/** Package-internal harness bridge. Not exported from package entry point. */\nexport interface HarnessToolSandbox {\n  readonly stagingRoot?: string;\n  readonly entryPath?: string;\n  readonly sourceFiles: readonly string[];\n  readonly executionContext: InternalExecutionContext;\n  dispose(): Promise<void>;\n}\n\n/** Package-internal harness bridge. Stages one immutable source graph. */\nexport async function prepareHarnessToolSandbox(\n  definition: AgentDefinition,\n  options: Pick<RunOptions, \"rootDir\" | \"entryPath\">,\n): Promise<HarnessToolSandbox> {\n  const executionContext = rootExecutionContext(definition);\n  if (options.entryPath === undefined &&\n      requiresSandboxEntry(definition, executionContext.sandboxRequired)) {\n    throw new Error(\n      \"cave_tool_sandbox_entry_required: sandboxed tools need entryPath\",\n    );\n  }\n  if (options.entryPath === undefined) {\n    return {\n      sourceFiles: Object.freeze([]),\n      executionContext,\n      async dispose() {},\n    };\n  }\n  const rootDir = options.rootDir ?? process.cwd();\n  const requested = resolve(rootDir, options.entryPath);\n  const snapshot = await stageSandboxSourceGraph(\n    rootDir,\n    requested,\n    dirname(fileURLToPath(import.meta.url)),\n  );\n  return {","sourceCodeStart":868,"sourceCodeEnd":904,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/runtime.ts#L868-L904","documentation":"Thrown by the package-internal harness bridge prepareHarnessToolSandbox() when the agent graph requires sandboxed tool execution (definition.sandbox === \"required\" or inherited from an ancestor, with at least one non-subagent tool in the graph) but options.entryPath is undefined. The sandbox stages an immutable copy of the source graph and imports tool workers only from that copy, so it needs an entry point to stage from. Without entryPath there is nothing to sandbox and required-sandbox execution must fail closed rather than run tools unsandboxed.","triggerScenarios":"Calling prepareHarnessToolSandbox(definition, { rootDir }) with no entryPath while definition.sandbox === \"required\" (or any subagent in the tool graph inherits required sandbox and has non-subagent tools).","commonSituations":"Migrating a test harness from optional to required sandbox and forgetting the entryPath option; a subagent deep in the tool graph declaring sandbox: \"required\", which propagates the requirement up to the root bridge call.","solutions":["Pass options.entryPath pointing at the entry module of the source graph to stage, e.g. prepareHarnessToolSandbox(def, { entryPath: \"src/main.ts\" })","If the tools genuinely need host access, declare sandbox: \"host\" on the definition instead of \"required\" (explicit opt-in; host mode runs closures in-process with no entryPath)","If sandboxing is not intended, remove sandbox: \"required\" from the definition and any nested subagent definitions"],"exampleFix":"// before\nconst sandbox = await prepareHarnessToolSandbox(definition, { rootDir });\n\n// after\nconst sandbox = await prepareHarnessToolSandbox(definition, {\n  rootDir,\n  entryPath: \"src/tools-entry.ts\",\n});","handlingStrategy":"validation","validationCode":"// Before calling the harness bridge: does the graph need an entry path?\nimport { readFileSync } from \"node:fs\";\nfunction graphNeedsEntry(definition: AgentDefinition, inherited = false): boolean {\n  const required = inherited || definition.sandbox === \"required\";\n  return definition.tools.some(t =>\n    t.runtime?.kind !== \"subagent\" ? required\n    : graphNeedsEntry(t.runtime.definition, required),\n  );\n}\nif (graphNeedsEntry(definition) && !options.entryPath) {\n  throw new Error(\"entryPath required for this sandboxed graph\");\n}","typeGuard":"const needsSandboxEntry = (def: AgentDefinition): boolean =>\n  def.sandbox === \"required\" || def.tools.some(t =>\n    t.runtime?.kind === \"subagent\" && needsSandboxEntry(t.runtime.definition));","tryCatchPattern":null,"preventionTips":["Treat entryPath as mandatory whenever sandbox: \"required\" appears anywhere in the graph","Add a unit test that walks the definition graph for sandbox requirements","Consider sandbox: \"host\" for interactive agents that need real host access"],"tags":["sandbox","harness","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}