JuliusBrussee/caveman · error · Error
caveman agent: unknown tool effect ${JSON.stringify(options.
Error message
caveman agent: unknown tool effect ${JSON.stringify(options.effect)} What it means
tool() requires an effect of exactly one of "read", "write", "idempotent", or "external" — the effect taxonomy the runtime uses for planning, replay, and permission decisions. Any other string (including casing variants like "Read", typos like "readonly", or "side-effect") throws immediately with the invalid value embedded.
Source
Thrown at packages/agent/src/primitives.ts:132
export function tool<TInput extends TSchema, TResult>(
options: ToolOptions<TInput, TResult>,
): ToolDefinition<Static<TInput>, TResult>;
export function tool<Input, Output, TResult>(
options: StandardToolOptions<Input, Output, TResult>,
): ToolDefinition<Output, TResult>;
export function tool<Input, Output, TResult>(
options: StandardJSONToolOptions<Input, Output, TResult>,
): ToolDefinition<Output, TResult>;
export function tool(
options: ToolOptions<TSchema, unknown> |
StandardToolOptions<unknown, unknown, unknown> |
StandardJSONToolOptions<unknown, unknown, unknown>,
): ToolDefinition<unknown, unknown> {
if (!/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(options.name)) {
throw new Error(`caveman agent: invalid tool name ${JSON.stringify(options.name)}`);
}
if (!["read", "write", "idempotent", "external"].includes(options.effect)) {
throw new Error(`caveman agent: unknown tool effect ${JSON.stringify(options.effect)}`);
}
const result = typeof options.result === "object"
? artifactResultPolicy(options.result)
: options.result ?? "auto";
if (!["auto", "inline", "page", "compress", "exact_ccr"].includes(result)) {
throw new Error(`caveman agent: unknown tool result policy ${JSON.stringify(result)}`);
}
const timeoutMs = options.timeoutMs ?? 30_000;
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
throw new Error("caveman agent: tool timeoutMs must be a positive integer");
}
const standard = standardToolSchema(options.input);
let input: TSchema;
if (standard === undefined) {
input = options.input as TSchema;
} else {
let converted = "inputJSONSchema" in options
? options.inputJSONSchemaView on GitHub (pinned to 27d5a3981a)
Solutions
- Use one of the four exact literals: "read", "write", "idempotent", "external"
- Pick by side-effect profile: no state change -> "read"; repeated same-call is safe -> "idempotent"; state changes and is not safely repeatable -> "write"; external system interaction -> "external"
- If migrating, map old vocabularies explicitly through a lookup table with a fallback that throws your own descriptive error
Example fix
// before
const t = tool({ name: "save", effect: "readonly", execute: ... });
// after
const t = tool({ name: "save", effect: "write", execute: ... }); Defensive patterns
Strategy: type-guard
Validate before calling
const EFFECTS = ["read", "write", "idempotent", "external"] as const;
if (!EFFECTS.includes(cfg.effect)) {
throw new Error(`effect must be one of ${EFFECTS.join(", ")}, got ${JSON.stringify(cfg.effect)}`);
} Type guard
type ToolEffect = "read" | "write" | "idempotent" | "external"; const isToolEffect = (v: unknown): v is ToolEffect => typeof v === "string" && ["read", "write", "idempotent", "external"].includes(v);
Prevention
- Type your tool option objects with the literal union so the compiler rejects bad effects
- When migrating frameworks, map effect vocabularies through an explicit table
- Never make effect optional in your own tool-building helpers
When it happens
Trigger: Passing effect: "readonly", "Read", "pure", "mutation", "io", or undefined. The field is mandatory with no default; omitting it entirely surfaces as an invalid undefined value in this same error.
Common situations: Migrating from another agent framework whose effect vocabulary differs (e.g. "destructive"/"safe"), assuming case-insensitivity, or optional-spread code paths that accidentally drop the field.
Related errors
- caveman agent: file path is required
- withTask: a callback is required
- cave_breaker_threshold_invalid
- caveman agent: subagent maxCalls must be a positive integer
- caveman agent: invalid tool name ${JSON.stringify(options.na
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/7f673f636c29961e.
Report an issue: GitHub.