{"record":{"id":"4a0f3bf576b55526","repo":"JuliusBrussee/caveman","slug":"cave-execution-authorization-private","errorCode":null,"errorMessage":"cave_execution_authorization_private","messagePattern":"cave_execution_authorization_private","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/claude-runtime.ts","lineNumber":591,"sourceCode":"  // OAuth subscription runs still expose provider token counts, but catalog\n  // list price is not money this run paid. Unknown auth also fails closed to\n  // the honest zero rather than guessing a metered credential.\n  return { ...usage, priced: false, catalogCostUsd: 0 };\n}\n\nfunction integer(value: unknown): number {\n  return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : 0;\n}\n\nfunction unprefixClaudeTool(name: string): string {\n  const prefix = \"mcp__caveman_agent__\";\n  return name.startsWith(prefix) ? name.slice(prefix.length) : name;\n}\n\nfunction rejectInternalOptions(options: ClaudeRunOptions): void {\n  const value = options as Record<string, unknown>;\n  if (\"lockedBuild\" in value || \"candidatePlan\" in value || \"queryFn\" in value) {\n    throw new Error(\"cave_execution_authorization_private\");\n  }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","sourceCodeStart":573,"sourceCodeEnd":598,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/claude-runtime.ts#L573-L598","documentation":"rejectInternalOptions() guards the public Claude run options: lockedBuild, candidatePlan, and queryFn are internal fields reserved for the framework's own build/execution pipeline (locked builds, candidate evaluation, recorded query injection). A public caller passing any of them is attempting to use private authorization-adjacent machinery, which is refused so callers cannot forge a locked-build execution or smuggle a fake query stream.","triggerScenarios":"Calling the public run API with an options object that includes lockedBuild, candidatePlan, or queryFn — even set to undefined via spread from an internal type, or copied from framework-internal code into application code.","commonSituations":"Copy-pasting example code from the framework's internal executor into an app; using `as any` to satisfy a type error by passing internal fields; structurally cloning an internal run options object and handing it to the public entry point.","solutions":["Remove lockedBuild/candidatePlan/queryFn from the options you pass; construct a fresh public options object with only documented fields.","If you genuinely need locked/candidate execution, go through the framework's build pipeline (caveman-agent build / dev), not the public run API.","If a spread is pulling them in, destructure them out: const { lockedBuild, candidatePlan, queryFn, ...public } = internal;"],"exampleFix":"// before\nawait run({ prompt, queryFn: fakeQuery, maxBudgetUsd: 1 });\n\n// after\nawait run({ prompt, maxBudgetUsd: 1 });","handlingStrategy":"validation","validationCode":"const INTERNAL_OPTION_KEYS = [\"lockedBuild\", \"candidatePlan\", \"queryFn\"] as const;\nfunction assertPublicOptions(options: Record<string, unknown>): void {\n  for (const key of INTERNAL_OPTION_KEYS) {\n    if (key in options) throw new Error(`option '${key}' is framework-internal`);\n  }\n}","typeGuard":"function isPublicRunOptions(value: unknown): value is ClaudeRunOptions {\n  if (typeof value !== \"object\" || value === null) return false;\n  const keys = Object.keys(value);\n  return !keys.includes(\"lockedBuild\") && !keys.includes(\"candidatePlan\") && !keys.includes(\"queryFn\");\n}","tryCatchPattern":null,"preventionTips":["Never construct run options by cloning internal framework objects; build them explicitly from documented fields.","Avoid `as any` on options — it is how internal fields sneak past TypeScript into the runtime guard.","If a type error pushes you toward lockedBuild/candidatePlan/queryFn, you want the build pipeline, not the public run API."],"tags":["api-misuse","security","authorization","options"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}