JuliusBrussee/caveman · error
cave_sandbox_request_invalid
cave_sandbox_request_invalid
Error message
cave_sandbox_request_invalid
What it means
The tool worker validates the stdin request frame against a strict shape before doing anything: `entry` must be a string; `agentPath` an array of at most 8 strings matching /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/; both digests 64-char lowercase hex; `tool` a name-shaped string; and `allowSideEffects`/`allowNetwork` booleans. Any deviation throws cave_sandbox_request_invalid — the protocol fails closed on malformed input because the worker is a trust boundary.
Source
Thrown at packages/agent/src/tool-worker.ts:101
}
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
}
try {
const request = await readRequest();
if (typeof request.entry !== "string" || !Array.isArray(request.agentPath) ||
request.agentPath.length > 8 ||
request.agentPath.some((item) => typeof item !== "string" ||
!/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(item)) ||
typeof request.rootDefinitionSha256 !== "string" ||
!/^[a-f0-9]{64}$/.test(request.rootDefinitionSha256) ||
typeof request.toolDefinitionSha256 !== "string" ||
!/^[a-f0-9]{64}$/.test(request.toolDefinitionSha256) ||
typeof request.tool !== "string" ||
!/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(request.tool) ||
typeof request.allowSideEffects !== "boolean" ||
typeof request.allowNetwork !== "boolean") {
throw new Error("cave_sandbox_request_invalid");
}
if (request.allowNetwork !== true) installNetworkDeny();
const imported = await import(request.entry) as { default?: AgentDefinition; agent?: AgentDefinition };
let definition = imported.default ?? imported.agent;
if (!definition || definition.kind !== "agent") throw new Error("cave_sandbox_agent_export_missing");
validateAgentGraph(definition);
if (agentDefinitionSHA256(definition) !== request.rootDefinitionSha256) {
throw new Error("cave_sandbox_definition_mismatch");
}
const visited = new Set<AgentDefinition>([definition]);
for (const name of request.agentPath) {
const delegated = definition.tools.filter((item) =>
item.name === name && item.runtime?.kind === "subagent"
);
if (delegated.length !== 1) throw new Error("cave_sandbox_unknown_subagent");
const child = delegated[0]!.runtime!.definition as AgentDefinition;
if (!child || child.kind !== "agent") {
throw new Error("cave_sandbox_subagent_definition_invalid");View on GitHub (pinned to 27d5a3981a)
Solutions
- Reinstall/rebuild so the runtime and tool worker come from the same package version.
- If invoking the worker manually, replicate the documented frame exactly (string entry, ≤8-segment agentPath, 64-hex digests, name-shaped tool, boolean flags).
- Reduce subagent nesting depth so agentPath stays within 8 segments.
Example fix
// before: manual test frame
{ entry: 42, agentPath: ["a","b","c","d","e","f","g","h","i"], ... }
// after
{ entry: "./dist/agent.js", agentPath: ["a","b"], rootDefinitionSha256: "<64 hex>", toolDefinitionSha256: "<64 hex>", tool: "myTool", params: {}, allowSideEffects: false, allowNetwork: false } Defensive patterns
Strategy: type-guard
Validate before calling
function isHex64(v: unknown): v is string { return typeof v === "string" && /^[a-f0-9]{64}$/.test(v); }
const NAME = /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/; Type guard
type SandboxRequest = { entry: string; agentPath: string[]; rootDefinitionSha256: string; toolDefinitionSha256: string; tool: string; params: unknown; allowSideEffects: boolean; allowNetwork: boolean };
function isSandboxRequest(r: unknown): r is SandboxRequest {
if (typeof r !== "object" || r === null) return false;
const v = r as Record<string, unknown>;
return typeof v.entry === "string" &&
Array.isArray(v.agentPath) && v.agentPath.length <= 8 &&
v.agentPath.every((s) => typeof s === "string" && /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(s)) &&
typeof v.rootDefinitionSha256 === "string" && /^[a-f0-9]{64}$/.test(v.rootDefinitionSha256) &&
typeof v.toolDefinitionSha256 === "string" && /^[a-f0-9]{64}$/.test(v.toolDefinitionSha256) &&
typeof v.tool === "string" && /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(v.tool) &&
typeof v.allowSideEffects === "boolean" && typeof v.allowNetwork === "boolean";
} Try / catch
try {
await runWorker(frame);
} catch (error) {
if (error instanceof Error && error.message === "cave_sandbox_request_invalid") {
// frame rejected at the trust boundary: check schema and version parity, never resend unchanged
} else throw error;
} Prevention
- Keep runtime and tool worker on the same package version — reinstall after upgrades.
- Validate hand-built frames with the full shape guard before writing to the worker's stdin.
- Keep agentPath at most 8 segments and tool/agent names matching [a-zA-Z][a-zA-Z0-9_-]{0,127}.
When it happens
Trigger: A parent runtime and worker built from mismatched versions exchanging different frame schemas; a hand-rolled or corrupted stdin write to the worker; agentPath longer than 8 levels or containing invalid characters.
Common situations: Version drift after a partial upgrade of @caveman-ai/agent (stale compiled worker, mixed node_modules); invoking the worker binary directly for testing with an ad-hoc JSON payload; deeply nested subagent paths exceeding the depth-8 path cap.
Related errors
- cave_live_eval_sandbox_profile_missing
- cave_live_eval_sandbox_profile_invalid
- caveman agent: unknown sandbox mode ${JSON.stringify(sandbox
- cave_sandbox_credential_capability_ambiguous
- cave_tool_sandbox_entry_required
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/33ae5f3b1f108c5a.
Report an issue: GitHub.