JuliusBrussee/caveman · error

cave_tool_sandbox_entry_required

cave_tool_sandbox_entry_required

Error message

cave_tool_sandbox_entry_required: sandboxed tools need entryPath

What it means

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.

Source

Thrown at packages/agent/src/runtime.ts:886

/** Package-internal harness bridge. Not exported from package entry point. */
export interface HarnessToolSandbox {
  readonly stagingRoot?: string;
  readonly entryPath?: string;
  readonly sourceFiles: readonly string[];
  readonly executionContext: InternalExecutionContext;
  dispose(): Promise<void>;
}

/** Package-internal harness bridge. Stages one immutable source graph. */
export async function prepareHarnessToolSandbox(
  definition: AgentDefinition,
  options: Pick<RunOptions, "rootDir" | "entryPath">,
): Promise<HarnessToolSandbox> {
  const executionContext = rootExecutionContext(definition);
  if (options.entryPath === undefined &&
      requiresSandboxEntry(definition, executionContext.sandboxRequired)) {
    throw new Error(
      "cave_tool_sandbox_entry_required: sandboxed tools need entryPath",
    );
  }
  if (options.entryPath === undefined) {
    return {
      sourceFiles: Object.freeze([]),
      executionContext,
      async dispose() {},
    };
  }
  const rootDir = options.rootDir ?? process.cwd();
  const requested = resolve(rootDir, options.entryPath);
  const snapshot = await stageSandboxSourceGraph(
    rootDir,
    requested,
    dirname(fileURLToPath(import.meta.url)),
  );
  return {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Pass options.entryPath pointing at the entry module of the source graph to stage, e.g. prepareHarnessToolSandbox(def, { entryPath: "src/main.ts" })
  2. 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)
  3. If sandboxing is not intended, remove sandbox: "required" from the definition and any nested subagent definitions

Example fix

// before
const sandbox = await prepareHarnessToolSandbox(definition, { rootDir });

// after
const sandbox = await prepareHarnessToolSandbox(definition, {
  rootDir,
  entryPath: "src/tools-entry.ts",
});
Defensive patterns

Strategy: validation

Validate before calling

// Before calling the harness bridge: does the graph need an entry path?
import { readFileSync } from "node:fs";
function graphNeedsEntry(definition: AgentDefinition, inherited = false): boolean {
  const required = inherited || definition.sandbox === "required";
  return definition.tools.some(t =>
    t.runtime?.kind !== "subagent" ? required
    : graphNeedsEntry(t.runtime.definition, required),
  );
}
if (graphNeedsEntry(definition) && !options.entryPath) {
  throw new Error("entryPath required for this sandboxed graph");
}

Type guard

const needsSandboxEntry = (def: AgentDefinition): boolean =>
  def.sandbox === "required" || def.tools.some(t =>
    t.runtime?.kind === "subagent" && needsSandboxEntry(t.runtime.definition));

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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