{"record":{"id":"8e615fcb0fab07e0","repo":"JuliusBrussee/caveman","slug":"cave-claude-max-turns-invalid","errorCode":"cave_claude_max_turns_invalid","errorMessage":"cave_claude_max_turns_invalid","messagePattern":"cave_claude_max_turns_invalid","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/claude-runtime.ts","lineNumber":107,"sourceCode":"    throw new Error(\"cave_claude_candidate_execution_unavailable\");\n  }\n  if (options.lockedBuild !== undefined) {\n    throw new Error(\"cave_claude_locked_execution_unavailable\");\n  }\n  if (definition.memory !== undefined) {\n    throw new Error(\"cave_claude_memory_bridge_unavailable\");\n  }\n  if (definition.tools.some((item) => item.runtime?.kind === \"subagent\")) {\n    throw new Error(\"cave_claude_subagent_bridge_unavailable\");\n  }\n  const unsupportedTool = definition.tools.find((item) =>\n    item.effect !== \"read\" || item.result !== \"inline\");\n  if (unsupportedTool !== undefined) {\n    throw new Error(`cave_claude_tool_contract_unsupported:${unsupportedTool.name}`);\n  }\n  if (options.maxTurns !== undefined &&\n      (!Number.isSafeInteger(options.maxTurns) || options.maxTurns <= 0 || options.maxTurns > 1_000)) {\n    throw new Error(\"cave_claude_max_turns_invalid\");\n  }\n  if (options.maxBudgetUsd !== undefined &&\n      (!Number.isFinite(options.maxBudgetUsd) || options.maxBudgetUsd <= 0)) {\n    throw new Error(\"cave_claude_max_budget_invalid\");\n  }\n  const rootDir = resolve(options.rootDir ?? process.cwd());\n  const lowered = await lowerAgentContext(definition, { rootDir, input });\n  const bill = contextBill(lowered.ir);\n  const selected = resolveClaudeModel(definition, rootDir);\n  const reasoningOptions = claudeReasoningOptions(\n    selected,\n    definition.reasoning,\n    definition.output?.maxTokens,\n  );\n  const sandbox = await prepareHarnessToolSandbox(definition, {\n    rootDir,\n    ...(options.entryPath === undefined ? {} : { entryPath: options.entryPath }),\n  });","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/claude-runtime.ts#L89-L125","documentation":"The options.maxTurns value passed to the Claude runtime is validated to be a safe integer strictly between 0 and 1000 (inclusive). Anything else — non-integer, zero, negative, above 1000, or non-numeric — throws this error before the agent runs.","triggerScenarios":"Calling runClaudeAgent/runClaudeAgentInternal with options.maxTurns set to 0, -1, 1001, NaN, 2.5, or a value exceeding Number.MAX_SAFE_INTEGER.","commonSituations":"Computing maxTurns from config or user input without clamping; passing 0 intending 'unlimited'; parsing a string like \"50\" from an env var without conversion.","solutions":["Set maxTurns to a positive safe integer within 1..1000","Clamp or validate the value before the call: Math.min(1000, Math.max(1, Number(maxTurns)))","Omit maxTurns entirely to use the runtime default"],"exampleFix":"// before\nawait runClaudeAgent({ ...opts, maxTurns: Number(process.env.MAX_TURNS) });\n// after\nconst turns = Number(process.env.MAX_TURNS);\nawait runClaudeAgent({ ...opts, maxTurns: Math.min(1000, Math.max(1, Math.trunc(turns))) });","handlingStrategy":"validation","validationCode":"if (opts.maxTurns !== undefined && (!Number.isSafeInteger(opts.maxTurns) || opts.maxTurns <= 0 || opts.maxTurns > 1000)) throw new RangeError(\"maxTurns must be an integer in 1..1000\");","typeGuard":"const isValidMaxTurns = (v) => v === undefined || (Number.isSafeInteger(v) && v > 0 && v <= 1000);","tryCatchPattern":"try { await runClaudeAgent(opts); } catch (e) { if (e.message === \"cave_claude_max_turns_invalid\") { opts.maxTurns = 10; return runClaudeAgent(opts); } throw e; }","preventionTips":["Clamp user/config-supplied turn counts to 1..1000","Never pass unparsed strings from env vars","Omit maxTurns when you want the default"],"tags":["validation","options","claude-runtime"],"backgroundTag":"value-out-of-range","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"}