{"record":{"id":"7f673f636c29961e","repo":"JuliusBrussee/caveman","slug":"caveman-agent-unknown-tool-effect-json-stringif","errorCode":null,"errorMessage":"caveman agent: unknown tool effect ${JSON.stringify(options.effect)}","messagePattern":"caveman agent: unknown tool effect (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/primitives.ts","lineNumber":132,"sourceCode":"export function tool<TInput extends TSchema, TResult>(\n  options: ToolOptions<TInput, TResult>,\n): ToolDefinition<Static<TInput>, TResult>;\nexport function tool<Input, Output, TResult>(\n  options: StandardToolOptions<Input, Output, TResult>,\n): ToolDefinition<Output, TResult>;\nexport function tool<Input, Output, TResult>(\n  options: StandardJSONToolOptions<Input, Output, TResult>,\n): ToolDefinition<Output, TResult>;\nexport function tool(\n  options: ToolOptions<TSchema, unknown> |\n    StandardToolOptions<unknown, unknown, unknown> |\n    StandardJSONToolOptions<unknown, unknown, unknown>,\n): ToolDefinition<unknown, unknown> {\n  if (!/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(options.name)) {\n    throw new Error(`caveman agent: invalid tool name ${JSON.stringify(options.name)}`);\n  }\n  if (![\"read\", \"write\", \"idempotent\", \"external\"].includes(options.effect)) {\n    throw new Error(`caveman agent: unknown tool effect ${JSON.stringify(options.effect)}`);\n  }\n  const result = typeof options.result === \"object\"\n    ? artifactResultPolicy(options.result)\n    : options.result ?? \"auto\";\n  if (![\"auto\", \"inline\", \"page\", \"compress\", \"exact_ccr\"].includes(result)) {\n    throw new Error(`caveman agent: unknown tool result policy ${JSON.stringify(result)}`);\n  }\n  const timeoutMs = options.timeoutMs ?? 30_000;\n  if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {\n    throw new Error(\"caveman agent: tool timeoutMs must be a positive integer\");\n  }\n  const standard = standardToolSchema(options.input);\n  let input: TSchema;\n  if (standard === undefined) {\n    input = options.input as TSchema;\n  } else {\n    let converted = \"inputJSONSchema\" in options\n      ? options.inputJSONSchema","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/primitives.ts#L114-L150","documentation":"tool() validates options.effect against the allow-list [\"read\", \"write\", \"idempotent\", \"external\"]. The effect declares the tool's side-effect class and is used for safety/policy decisions, so an unrecognized value is rejected immediately.","triggerScenarios":"tool({ effect: \"readonly\" }) instead of \"read\"; tool({ effect: \"mutate\" }) instead of \"write\"; misspellings like \"side-effect\"; effect left undefined when the options shape requires it.","commonSituations":"Typos or synonyms when hand-writing tool definitions; migrating from another framework whose effect vocabulary differs (e.g. \"pure\", \"destructive\"); conditional code building the effect string produces undefined.","solutions":["Set effect to exactly one of \"read\", \"write\", \"idempotent\", or \"external\"","Map legacy/foreign effect values to the closest allowed one before calling tool()","Add a compile-time union type (\"read\" | \"write\" | \"idempotent\" | \"external\") to catch typos at typecheck"],"exampleFix":"// before\ntool({ name: \"deploy\", effect: \"destructive\", ... })\n// after\ntool({ name: \"deploy\", effect: \"external\", ... })","handlingStrategy":"type-guard","validationCode":"type Effect = \"read\" | \"write\" | \"idempotent\" | \"external\";\nconst EFFECTS: readonly Effect[] = [\"read\", \"write\", \"idempotent\", \"external\"];\nfunction isEffect(v: unknown): v is Effect {\n  return typeof v === \"string\" && (EFFECTS as readonly string[]).includes(v);\n}","typeGuard":"function isEffect(v: unknown): v is \"read\" | \"write\" | \"idempotent\" | \"external\" {\n  return [\"read\", \"write\", \"idempotent\", \"external\"].includes(v as never);\n}","tryCatchPattern":"try {\n  const def = tool(options);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"unknown tool effect\")) {\n    // map options.effect to an allowed value and retry\n  } else throw err;\n}","preventionTips":["Always use the Effect union type instead of raw strings","Map third-party effect vocabularies explicitly before calling tool()","Keep effect values in one shared constant list"],"tags":["validation","enum","tool-definition"],"backgroundTag":"invalid-enum-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}