{"record":{"id":"f3e3720ec790c5d9","repo":"JuliusBrussee/caveman","slug":"cannot-render-text-as-a-stable-numeric-literal","errorCode":null,"errorMessage":"cannot render ${text} as a stable numeric literal","messagePattern":"cannot render (.+?) as a stable numeric literal","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/generate-agent-catalog.mjs","lineNumber":216,"sourceCode":"      }\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 }) => [\n    `  ${JSON.stringify(key)}: Object.freeze({`,\n    `    inputPerMillion: ${number(price.inputPerMillion)},`,\n    `    outputPerMillion: ${number(price.outputPerMillion)},`,\n    `    cacheReadPerMillion: ${number(price.cacheReadPerMillion)},`,\n    `    cacheWritePerMillion: ${number(price.cacheWritePerMillion)},`,\n    `    reasoningPerMillion: ${number(price.reasoningPerMillion)},`,\n    `  }),`,\n  ].join(\"\\n\")).join(\"\\n\");","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/scripts/generate-agent-catalog.mjs#L198-L234","documentation":"Thrown by number() in scripts/generate-agent-catalog.mjs when a price that already passed rate() validation still renders as a string that does not match the canonical numeric-literal regex (no leading zeros, no '+', no capital 'E', no negative sign). This is a defensive invariant guarding deterministic output: the generated catalog.ts must be byte-stable so the drift test can recompute it. In practice it is nearly unreachable because rate() already rejected negatives and non-finite values.","triggerScenarios":"Only reachable through an internal inconsistency — e.g. a value whose String() form is non-canonical (negative zero rendering, exotic exponent forms) or a bug/hand-edit that bypasses rate(). Normal YAML input cannot produce it.","commonSituations":"Essentially none for catalog editors; would indicate a bug in the script itself or a hand-modified selected row in a test harness.","solutions":["If hit while editing the catalog, double-check the named price value is a plain decimal (e.g. 1.5, 0.25) and re-run.","If it reproduces with valid input, file a bug against scripts/generate-agent-catalog.mjs — rate() and number() have disagreeing notions of a valid number."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  renderCatalogModule(bytes);\n} catch (error) {\n  if (error.message.startsWith(\"cannot render\")) throw new BugError(\"generate-agent-catalog invariant broken\", { cause: error });\n  throw error;\n}","preventionTips":["Never hand-edit public/agent/src/catalog.ts — regenerate it from the YAML.","If this fires with valid input, report it: rate() and number() disagree, which is a script bug."],"tags":["codegen","invariant","catalog","numbers"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}