JuliusBrussee/caveman · error
cave_host_sandbox_nested_under_required
cave_host_sandbox_nested_under_required
Error message
cave_host_sandbox_nested_under_required
What it means
Thrown by visit in validateAgentGraph when a nested agent definition declares sandbox: "host" while an ancestor in the chain has already required sandboxing (sandbox: "required" propagates to children via childSandboxRequired). Host mode would let the nested closure escape containment that an ancestor mandated, so it is rejected.
Solutions
- Change the nested agent's sandbox from "host" to "required" (or another contained mode).
- Remove sandbox: "required" from the ancestor if host execution is genuinely intended for the whole tree.
- Replace the host-sandboxed subagent with a tool that performs the needed work inside the sandbox.
Example fix
// before
const nested = { kind: "agent", sandbox: "host", tools: [] };
const root = { kind: "agent", sandbox: "required", tools: [subagentTool(nested)] };
// after
const nested = { kind: "agent", sandbox: "required", tools: [] };
const root = { kind: "agent", sandbox: "required", tools: [subagentTool(nested)] }; Defensive patterns
Strategy: validation
Validate before calling
function auditsHostSandbox(def: AgentDefinition, required = false): boolean {
if (required && def.sandbox === "host") return false;
const childRequired = required || def.sandbox === "required";
return (def.tools ?? []).every(t => t.runtime?.kind !== "subagent" || auditsHostSandbox(t.runtime.definition, childRequired));
}
if (!auditsHostSandbox(root)) throw new Error("host-sandboxed agent nested under required sandbox"); Try / catch
try {
validateAgentGraph(root);
} catch (e) {
if (e instanceof Error && e.message === "cave_host_sandbox_nested_under_required") {
console.error("A nested agent uses sandbox: 'host' under a required-sandbox ancestor; set the child to 'required'");
} else throw e;
} Prevention
- When the root requires sandboxing, audit every nested third-party definition for sandbox: "host".
- Default nested agents to sandbox: "required" to inherit safely.
- Re-validate the whole tree after changing any ancestor's sandbox setting.
When it happens
Trigger: A root or ancestor agent sets sandbox: "required", and any descendant definition (reached through a subagent tool's runtime.definition) sets sandbox: "host".
Common situations: Composing a sandboxed root agent with third-party subagent definitions that default to host mode, or upgrading a root to sandbox: "required" without auditing existing nested agents.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- cave_live_eval_sandbox_profile_escapes_root
- cave_sandbox_child_process_containment_unavailable
- cave_sandbox_conformance_failed
- cave_sandbox_credential_capability_ambiguous
- cave_sandbox_credential_env_not_allowlisted
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/501c0aeb57a9fd01.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/definition-graph.ts:27
// reached under a sandbox-required ancestor must be re-checked, not skipped.
const visited = [new Set<AgentDefinition>(), new Set<AgentDefinition>()];
const active = new Set<AgentDefinition>();
const visit = (
definition: AgentDefinition,
depth: number,
sandboxRequired: boolean,
): void => {
if (!definition || definition.kind !== "agent" ||
!Array.isArray(definition.tools)) {
throw new Error("cave_agent_definition_invalid");
}
if (depth > 8) throw new Error("cave_subagent_depth_limit");
if (active.has(definition)) throw new Error("cave_subagent_definition_cycle");
// Host mode is an opt-in the root makes for itself. A descendant cannot use
// it to run closures outside an ancestor's required containment.
if (sandboxRequired && definition.sandbox === "host") {
throw new Error("cave_host_sandbox_nested_under_required");
}
const memo = visited[sandboxRequired ? 1 : 0]!;
if (memo.has(definition)) return;
active.add(definition);
const childSandboxRequired = sandboxRequired ||
definition.sandbox === "required";
const names = new Set<string>();
for (const declared of definition.tools) {
if (!declared || declared.kind !== "tool" ||
typeof declared.name !== "string") {
throw new Error("cave_tool_definition_invalid");
}
if (names.has(declared.name)) throw new Error("cave_duplicate_tool_name");
names.add(declared.name);
if (declared.name.startsWith("cave_")) {
throw new Error(`cave_reserved_tool_name:${declared.name}`);
}
if (typeof Reflect.get(declared, TOOL_IMPLEMENTATION_SOURCE) !== "string") {View on GitHub (pinned to 3ee70a1026)