JuliusBrussee/caveman · error
cave_sandbox_failure_not_serializable
cave_sandbox_failure_not_serializable
Error message
cave_sandbox_failure_not_serializable
What it means
The tool worker writes exactly one result frame to fd 3; if serializing a FAILURE payload ({ ok: false, ... }) with JSON.stringify throws (e.g. a non-serializable property like BigInt or a circular reference on the error), writeResult throws cave_sandbox_failure_not_serializable — it cannot even produce a fallback failure frame. This is a last-resort invariant: terminal state is only marked after serialization succeeds so a bad value can never corrupt the protocol into an opaque invalid-output.
Source
Thrown at packages/agent/src/tool-worker.ts:26
import type { AgentDefinition } from "./index.js";
import { installNetworkDeny } from "./sandbox-network.js";
// The result travels on a DEDICATED fd 3, length-prefixed — never stdout
// Any console.log in the tool's own import graph writes to stdout
// (fd 1) and the parent ignores it, so it can no longer collide with the result
// JSON and corrupt it into cave_sandbox_invalid_output.
let resultWritten = false;
function writeResult(payload: { ok: boolean; value?: unknown; code?: string }): void {
if (resultWritten) return;
let encoded: string;
try {
encoded = JSON.stringify(payload);
} catch {
if (payload.ok) {
writeResult({ ok: false, code: "cave_sandbox_result_not_serializable" });
return;
}
throw new Error("cave_sandbox_failure_not_serializable");
}
// Mark terminal only after serialization succeeds. Otherwise a BigInt or
// circular tool value suppresses the failure frame and becomes an opaque
// cave_sandbox_invalid_output at the parent.
resultWritten = true;
const body = Buffer.from(encoded, "utf8");
const header = Buffer.allocUnsafe(4);
header.writeUInt32BE(body.byteLength, 0);
const frame = Buffer.concat([header, body]);
let offset = 0;
while (offset < frame.byteLength) {
offset += writeSync(3, frame, offset, frame.byteLength - offset);
}
}
function failureCode(error: unknown): string {
return error instanceof Error
? [View on GitHub (pinned to 27d5a3981a)
Solutions
- Sanitize thrown errors in tool code: keep message a string and drop or String() non-serializable custom properties.
- If you attach context to errors, attach primitives or serializable summaries only.
- Reproduce locally by running the tool in-process and inspecting the thrown error's own enumerable properties.
Example fix
// before
throw Object.assign(new Error("bad value"), { value: 10n });
// after
throw new Error(`bad value: ${10n}`); Defensive patterns
Strategy: fallback
Validate before calling
function serializableError(err: unknown): boolean {
try { JSON.stringify({ m: (err as Error)?.message, c: (err as Error)?.cause }); return true; }
catch { return false; }
} Type guard
function isSerializable(value: unknown): boolean {
try { JSON.stringify(value); return true; } catch { return false; }
} Try / catch
// In tool code — sanitize before throwing from a sandboxed worker:
try {
risky();
} catch (error) {
throw new Error(error instanceof Error ? String(error.message) : String(error));
} Prevention
- Throw plain Error objects with string messages from sandboxed tools.
- Never attach BigInt, circular structures, or live handles to error properties.
- Stringify non-serializable context before putting it on an error.
When it happens
Trigger: A sandboxed tool throws an Error whose own properties (message, cause, custom fields) contain BigInt, circular structures, or other JSON-hostile values carried onto the failure payload.
Common situations: Custom error classes attaching the offending value itself (e.g. error.value = someBigInt); rethrowing provider/tool objects with cycles; almost never hit because success payloads convert to cave_sandbox_result_not_serializable instead.
Related errors
- cave_sandbox_input_limit
- cave_sandbox_request_invalid
- cave_harness_wire_contract_invalid
- probe returned false
- cave_sandbox_conformance_failed
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/a497b723a93beb3c.
Report an issue: GitHub.