JuliusBrussee/caveman · error

cave_sandbox_source_staging_root_required

cave_sandbox_source_staging_root_required

Error message

cave_sandbox_source_staging_root_required

What it means

commonAncestorDir throws this when its paths array is empty (dirs[0] is undefined). In the production flow it is only reached from sandboxSourceReadFlags after the >1024-file threshold, which implies a non-empty list, so an empty array here means an internal invariant broke: the flag-collapse path executed with zero staged source files. It is a defensive guard, not a configuration error surface.

Source

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

    await rm(workspace, { recursive: true, force: true });
  }
}

/**
 * Above this many per-file `--allow-fs-read` flags, collapse the staged source
 * files to their common ancestor directory. A large project would
 * otherwise blow the OS argument limit (E2BIG) and the tool could not spawn at
 * all. The collapse is safe here: `sourceFiles` are paths inside the per-run
 * STAGED COPY, which already contains only the reachable source graph — never
 * the real project root with its .env and credentials.
 */
const SANDBOX_FS_READ_FLAG_THRESHOLD = 1024;

function commonAncestorDir(paths: readonly string[]): string {
  const dirs = paths.map((path) => resolve(dirname(path)));
  const first = dirs[0];
  if (first === undefined) {
    throw new Error("cave_sandbox_source_staging_root_required");
  }
  let ancestor = first;
  while (dirs.some((path) => escapesRoot(relative(ancestor, path)))) {
    const parent = dirname(ancestor);
    if (parent === ancestor) {
      throw new Error("cave_sandbox_source_read_root_refused");
    }
    ancestor = parent;
  }
  if (dirname(ancestor) === ancestor) {
    throw new Error("cave_sandbox_source_read_root_refused");
  }
  return ancestor;
}

export function sandboxSourceReadFlags(
  sourceFiles: readonly string[],
  stagingRoot?: string,

View on GitHub (pinned to 766dce6b13)

Solutions

  1. Ensure the tool declares at least one source file before computing read flags
  2. If invoking sandboxSourceReadFlags directly, guard the empty list yourself or stay under SANDBOX_FS_READ_FLAG_THRESHOLD (1024)
  3. If hit through the public API, report it as a bug: the executor should never collapse an empty graph
Defensive patterns

Strategy: validation

Validate before calling

// Guard the empty list before computing collapsed read flags
function safeSourceReadFlags(sourceFiles: readonly string[], stagingRoot?: string) {
  if (sourceFiles.length === 0) return []; // nothing to grant; avoids the invariant throw
  return sandboxSourceReadFlags(sourceFiles, stagingRoot);
}

Type guard

function hasSourceFiles(files: readonly string[]): boolean {
  return Array.isArray(files) && files.length > 0 && files.every((f) => typeof f === 'string' && f.length > 0);
}

Try / catch

try {
  flags = sandboxSourceReadFlags(sourceFiles, stagingRoot);
} catch (error) {
  if (error instanceof Error && error.message === 'cave_sandbox_source_staging_root_required') {
    // empty list at collapse time: internal invariant - report with the file list
  }
  throw error;
}

Prevention

When it happens

Trigger: Directly calling sandboxSourceReadFlags/commonAncestorDir with an empty sourceFiles array in tests or internal tooling; a tool definition whose staged source graph is empty by the time the collapse runs (internal bug).

Common situations: Unit tests exercising the collapse helpers without fixtures; custom integrations importing the internals rather than going through the sandbox executor; effectively unreachable through the public run API.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/d1c86549241711cd. Report an issue: GitHub.