JuliusBrussee/caveman · error

cave_sandbox_side_effect_denied

cave_sandbox_side_effect_denied

Error message

cave_sandbox_side_effect_denied

What it means

The sandbox executes the selected tool only if its declared effect is "read", or the request explicitly set allowSideEffects to true. Any tool with effect "write" (or another non-read effect) under allowSideEffects !== true is refused before execute — this is the sandbox's side-effect containment guarantee.

Source

Thrown at packages/agent/src/tool-worker.ts:134

    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");
    }
    if (visited.has(child)) throw new Error("cave_sandbox_subagent_cycle");
    visited.add(child);
    definition = child;
  }
  const selectedTools = definition.tools.filter((item) => item.name === request.tool);
  if (selectedTools.length !== 1 || selectedTools[0]!.runtime?.kind === "subagent") {
    throw new Error("cave_sandbox_unknown_tool");
  }
  const selected = selectedTools[0]!;
  if (toolDefinitionSHA256(selected) !== request.toolDefinitionSha256) {
    throw new Error("cave_sandbox_tool_definition_mismatch");
  }
  if (selected.effect !== "read" && request.allowSideEffects !== true) {
    throw new Error("cave_sandbox_side_effect_denied");
  }
  const value = await selected.execute(request.params as never, AbortSignal.timeout(selected.timeoutMs));
  writeResult({ ok: true, value });
} catch (error) {
  writeResult({ ok: false, code: failureCode(error) });
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. If the mutation is intended, spawn the worker request with allowSideEffects: true (and understand the sandbox then permits writes).
  2. If the tool is genuinely read-only, correct its declaration to effect:"read".
  3. Route unintended write attempts to a read-only equivalent tool in the parent's admission logic.

Example fix

// before
{ tool: "write_file", params: {...}, allowSideEffects: false }

// after
effect === "read"
  ? { tool: "write_file", params: {...} }
  : { tool: "write_file", params: {...}, allowSideEffects: true }
Defensive patterns

Strategy: validation

Validate before calling

const needsSideEffects = selected.effect !== "read";
if (needsSideEffects && request.allowSideEffects !== true) {
  throw new Error(`tool ${selected.name} declares effect=${selected.effect}; require explicit operator approval`);
}

Type guard

function isReadOnlyTool(t: { effect?: string }): boolean {
  return t.effect === "read";
}

Prevention

When it happens

Trigger: Calling a tool whose definition declares effect:"write" (or "side_effect") while the spawning request was built with allowSideEffects:false or omitted it; a run in read-only/observe mode attempting a mutating tool.

Common situations: Default sandbox policy is read-only and the developer forgot to opt into side effects for this run; a tool was misdeclared as write when it is actually read-only; CI runs pinned to allowSideEffects:false hitting a write tool.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/04b6f85efee55f31. Report an issue: GitHub.