{"record":{"id":"a265e5bb150e7ade","repo":"JuliusBrussee/caveman","slug":"caveman-agent-output-maxtokens-must-be-a-positive","errorCode":null,"errorMessage":"caveman agent: output maxTokens must be a positive integer","messagePattern":"caveman agent: output maxTokens must be a positive integer","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/primitives.ts","lineNumber":404,"sourceCode":"    kind: \"artifact\",\n    strategy: options.strategy ?? \"page\",\n    maxInlineTokens,\n    recovery: options.recovery ?? \"exact_ccr\",\n  });\n}\n\nexport interface OutputDefinition<TSchemaValue extends TSchema | undefined = TSchema | undefined> {\n  readonly kind: \"output\";\n  readonly maxTokens: number;\n  readonly schema?: TSchemaValue;\n}\n\nexport function output<T extends TSchema | undefined = undefined>(options: {\n  maxTokens: number;\n  schema?: T;\n}): OutputDefinition<T> {\n  if (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0) {\n    throw new Error(\"caveman agent: output maxTokens must be a positive integer\");\n  }\n  return Object.freeze({\n    kind: \"output\",\n    maxTokens: options.maxTokens,\n    ...(options.schema === undefined ? {} : { schema: options.schema }),\n  }) as OutputDefinition<T>;\n}\n\nexport type QualityGrader =\n  | { type: \"contains\"; fragments: string[] }\n  | { type: \"tool_called\"; tools: string[] }\n  | { type: \"exact_match\"; expected: string }\n  | { type: \"json_schema\"; schema: TSchema };\n\nexport type EvalGuardrail =\n  | { type: \"latency_threshold\"; p95_ms: number }\n  | { type: \"error_rate\"; max: number };\n","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/primitives.ts#L386-L422","documentation":"output() requires maxTokens to be a positive safe integer and throws when it is missing, zero, negative, fractional, or not a Number.isSafeInteger. maxTokens is mandatory (not optional) on OutputDefinition, so undefined also fails the check. This guarantees the output budget is a usable positive integer before any model call is made.","triggerScenarios":"output({ maxTokens: 0 }), output({ maxTokens: -1 }), a fractional value like 512.5, NaN/Infinity, or calling output({}) with maxTokens omitted entirely.","commonSituations":"A config value of 0 used to mean 'unlimited' (the API does not accept that); a parsed string that is not a number; multiplying by a factor that yields a float.","solutions":["Pass a positive integer, e.g. output({ maxTokens: 1024 }).","Round and floor any computed value before passing it, ensuring it is at least 1.","If the value is optional in your config, supply an explicit positive default rather than relying on the API."],"exampleFix":"// before\nconst out = output({ maxTokens: 0 });\n// after\nconst out = output({ maxTokens: 2048 });","handlingStrategy":"validation","validationCode":"function validMaxTokens(v) { return typeof v === \"number\" && Number.isSafeInteger(v) && v > 0; }\nif (!validMaxTokens(cfg.maxTokens)) throw new Error(`maxTokens must be a positive integer, got ${cfg.maxTokens}`);","typeGuard":"function isPositiveInt(v: unknown): v is number { return typeof v === \"number\" && Number.isSafeInteger(v) && v > 0; }","tryCatchPattern":"let out;\ntry { out = output({ maxTokens: cfg.maxTokens }); }\ncatch (e) { if (String(e.message).includes(\"maxTokens\")) out = output({ maxTokens: 2048 }); else throw e; }","preventionTips":["Treat 0 as invalid — there is no 'unlimited' sentinel for maxTokens.","Round computed token budgets and enforce a minimum of 1.","Validate output config at startup, before defining any outputs."],"tags":["validation","typescript","output","argument-validation"],"backgroundTag":"invalid-argument-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"}