{"record":{"id":"58315ec1cfeb97af","repo":"JuliusBrussee/caveman","slug":"label-field-must-be-a-finite-non-negative-n","errorCode":null,"errorMessage":"${label}: ${field} must be a finite non-negative number","messagePattern":"(.+?): (.+?) must be a finite non-negative number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/generate-agent-catalog.mjs","lineNumber":208,"sourceCode":"    });\n  }\n  selected.sort((left, right) => (left.key < right.key ? -1 : left.key > right.key ? 1 : 0));\n  const skipped = [...skippedByKey.entries()]\n    .map(([key, entry]) => {\n      const reasons = [...entry.reasons].sort();\n      if (entry.regions.length > 0) {\n        reasons.unshift(`priced per region only (${[...entry.regions].sort().join(\", \")})`);\n      }\n      return { key, reason: reasons.join(\"; \") };\n    })\n    .sort((left, right) => (left.key < right.key ? -1 : left.key > right.key ? 1 : 0));\n  return { selected, skipped };\n}\n\nfunction rate(value, field, label) {\n  if (value === undefined || value === null) return null;\n  if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n    throw new Error(`${label}: ${field} must be a finite non-negative number`);\n  }\n  return value;\n}\n\nfunction number(value) {\n  const text = String(value);\n  if (!/^(0|[1-9][0-9]*)(\\.[0-9]+)?(e[+-]?[0-9]+)?$/.test(text)) {\n    throw new Error(`cannot render ${text} as a stable numeric literal`);\n  }\n  return text;\n}\n\n/** Renders the full public/agent/src/catalog.ts module text. */\nexport function renderCatalogModule(catalogBytes, label = CATALOG_LABEL) {\n  const digest = createHash(\"sha256\").update(catalogBytes).digest(\"hex\");\n  const { selected, skipped } = selectRows(parseCatalogYaml(catalogBytes.toString(\"utf8\"), label), label);\n  if (selected.length === 0) throw new Error(`${label}: no priced region-agnostic rows found`);\n  const entries = selected.map(({ key, price }) => [","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/scripts/generate-agent-catalog.mjs#L190-L226","documentation":"Thrown by rate() in scripts/generate-agent-catalog.mjs when a pricing field inside a selected (global, USD) row is present but is not a finite non-negative number: negative values, NaN, Infinity, or non-number scalars (booleans, strings) all fail. Null and undefined are legal (they mean 'not offered' and cause the row to be skipped or default to 0 for cache rates), which is why only bad non-null values throw.","triggerScenarios":"A pricing field like `input_per_million: -1` (negative), `input_per_million: true`, `input_per_million: \"3\"` (quoted string), or a value that parsed to NaN. The field name in the message identifies exactly which rate on which provider/model is bad.","commonSituations":"Recording a discount as a negative number; quoting prices as strings when pasting from a spreadsheet; a `true`/`false` value left from experimentation; null-vs-0 confusion for cache rates.","solutions":["Fix the named field to a plain non-negative number (e.g. 0.25); use null (not 0) when the rate genuinely does not exist — the script deliberately maps null cache rates to 0 only for cache fields.","Remove quotes around numeric values in the YAML so they parse as numbers, not strings.","Re-run the generator to confirm the row validates."],"exampleFix":"# before\n  pricing:\n    input_per_million: \"1.5\"\n    output_per_million: -6\n# after\n  pricing:\n    input_per_million: 1.5\n    output_per_million: 6","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isValidRate(v) {\n  return v === undefined || v === null || (typeof v === \"number\" && Number.isFinite(v) && v >= 0);\n}","tryCatchPattern":null,"preventionTips":["Write prices as unquoted numeric literals; strings and booleans always fail.","Use null for 'rate not offered', never 0 or a negative placeholder."],"tags":["yaml","numbers","validation","pricing"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}