JuliusBrussee/caveman · error
cave_host_sandbox_lock_ineligible
cave_host_sandbox_lock_ineligible
Error message
cave_host_sandbox_lock_ineligible
What it means
compile refuses to produce a lock when the agent definition graph uses the host sandbox mode, because host sandboxes execute closures in the calling process and a saved lock would overstate containment. The entire definition graph is checked, not just the root, so a host subagent nested under a fixture root also triggers this. This is a hard error, never downgraded to a soft status.
Solutions
- Reconfigure the offending agent/subagents to a contained sandbox mode before compiling for a lock
- Inspect the whole agent graph (not just the root) for host sandbox usage and eliminate it
- If in-process execution is required, skip lock compilation for this agent and run it unlocked
- Split the lockable contained portion from host-executing portions into separate agents
Example fix
// before
compile({ agent: graphWithHostSubagent, evals, config }); // throws
// after
const contained = withSandboxMode(graphWithHostSubagent, "contained");
compile({ agent: contained, evals, config }); Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check the graph for host sandbox usage if the library exposes inspection
// otherwise treat compile as the check itself and avoid saving locks on throw
try { const result = compile(input); } catch (e) { /* lock ineligible */ } Try / catch
try {
const result = compile(input);
saveLock(result);
} catch (e) {
if (e instanceof Error && e.message === "cave_host_sandbox_lock_ineligible") {
// run unlocked; audit the agent graph for host sandbox subagents
} else throw e;
} Prevention
- Audit every subagent's sandbox mode, not just the root's
- Keep lock-target agents fully on contained sandbox modes
- Never assume a fixture root makes nested host subagents lockable
When it happens
Trigger: Calling compile with input.agent whose graph (root or any subagent/closure) resolves to a host sandbox mode — e.g. coding-agent closures running in-process — then attempting to save the compile result as a lock.
Common situations: Compiling an agent that embeds host-executing tool closures; a fixture-root agent that delegates to a host-mode subagent; switching a subagent's sandbox to host after a previously lockable config.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- MCP transaction state kept changing; refusing mutation
- cave_build_lock_missing
- cave_host_sandbox_nested_under_required
- cave_live_eval_sandbox_profile_escapes_root
- cave_live_eval_sandbox_profile_missing
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/a0ecc47d29ae412a.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/build.ts:227
evidence: Array<{ fixture: string; seed: number; value: RunEvidence }>;
costPerTask: number;
errorRate: number;
p95Latency: number;
fixturePassRate: number;
retentionLCB95: number;
passing: boolean;
};
export async function compile(input: CompileInput): Promise<CompileResult> {
// An unsandboxed run cannot produce a lock. Host mode runs tool
// closures in the host process, so its evidence shows no containment and is
// refused here rather than downgraded to a soft status. The whole definition
// graph is checked, not just the root: a host subagent under a fixture root
// runs its closures in this process exactly as a host root would, and the CLI
// saves that lock all the same. Coding agents lock by compiling against
// fixture corpora with a contained sandbox mode.
if (graphUsesHostSandbox(input.agent)) {
throw new Error("cave_host_sandbox_lock_ineligible");
}
const approved = input.evals.filter((fixture) => fixture.approved && fixture.required);
if (approved.length === 0) {
return emptyResult("needs_eval", "no approved required eval fixture");
}
const seeds = input.seeds ?? [1, 2, 3, 4, 5];
if (seeds.length === 0 || new Set(seeds).size !== seeds.length) {
throw new Error("caveman build: seeds must be a non-empty unique set");
}
const candidates = input.candidates ?? generateCandidatePlans(
input.agent,
input.contextIR,
input.baselinePlan,
input.modelCandidates ?? [input.baselinePlan.model],
true,
new Map(),
ENGINE_TRANSFORM_CAPABILITIES,
new Set(input.evals.some((fixture) =>View on GitHub (pinned to 3ee70a1026)