{"record":{"id":"c2ccaf2fed26aa48","repo":"Hmbown/CodeWhale","slug":"non-finite-number-in-payload","errorCode":null,"errorMessage":"non-finite number in payload","messagePattern":"non-finite number in payload","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/scripts/facts-publish.mjs","lineNumber":51,"sourceCode":"export const SCHEMA_VERSION = 1;\nexport const MAX_PAYLOAD_BYTES = 512 * 1024;\nexport const MAX_ENVELOPE_BYTES = 768 * 1024;\nconst KEY_ID_RE = /^cwf-[a-z0-9-]{1,32}$/;\nconst CHANNEL_RE = /^[a-z0-9][a-z0-9-]{0,31}$/;\nconst CI_MARKERS = [\"CI\", \"GITHUB_ACTIONS\", \"GITLAB_CI\", \"BUILDKITE\", \"CIRCLECI\", \"JENKINS_URL\", \"TF_BUILD\"];\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst WEB_ROOT = resolve(here, \"..\");\nconst REPO_ROOT = resolve(WEB_ROOT, \"..\");\n\n// ---------------------------------------------------------------------------\n// Canonical JSON + signing primitives (exported for tests)\n// ---------------------------------------------------------------------------\n\nexport function canonicalize(value) {\n  if (value === null || typeof value !== \"object\") {\n    if (typeof value === \"number\" && !Number.isFinite(value)) {\n      throw new Error(\"non-finite number in payload\");\n    }\n    return JSON.stringify(value);\n  }\n  if (Array.isArray(value)) return `[${value.map(canonicalize).join(\",\")}]`;\n  const keys = Object.keys(value).sort();\n  const parts = [];\n  for (const key of keys) {\n    const v = value[key];\n    if (v === undefined) continue;\n    parts.push(`${JSON.stringify(key)}:${canonicalize(v)}`);\n  }\n  return `{${parts.join(\",\")}}`;\n}\n\nexport function signingMessage(keyId, payloadBytes) {\n  return Buffer.concat([Buffer.from(DOMAIN, \"utf8\"), Buffer.from(keyId, \"utf8\"), Buffer.from([0]), payloadBytes]);\n}\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/web/scripts/facts-publish.mjs#L33-L69","documentation":"canonicalize in facts-publish.mjs produces canonical JSON for signing. JSON cannot represent NaN, Infinity, or -Infinity, so if it encounters a non-finite number while serializing it throws this error rather than emitting 'null' (JSON.stringify's silent default) which would break signature reproducibility.","triggerScenarios":"Calling canonicalize(value) where value — or any nested member — is NaN, Infinity, or -Infinity, e.g. buildPayload/serialize path given facts computed with a division by zero.","commonSituations":"Building facts from metrics computed with parseFloat('x'), 0/0, or Math.log(-1); JSON.stringify silently writes null but this canonicalizer refuses.","solutions":["Sanitize the payload before canonicalizing: replace or remove non-finite numbers","Use Number.isFinite() on every numeric field before building the payload","Store such values as strings or null explicitly if they must appear"],"exampleFix":"// before\nconst facts = { uptime: 1 / 0 };\ncanonicalize(facts);\n// after\nconst facts = { uptime: Number.isFinite(raw) ? raw : null };\ncanonicalize(facts);","handlingStrategy":"validation","validationCode":"function assertFiniteNumbers(obj) {\n  for (const [k, v] of Object.entries(obj)) {\n    if (typeof v === 'number' && !Number.isFinite(v)) throw new Error(`${k} is not finite`);\n    if (v && typeof v === 'object') assertFiniteNumbers(v);\n  }\n}\nassertFiniteNumbers(payload);","typeGuard":"const isFiniteNumber = (v) => typeof v === 'number' && Number.isFinite(v);","tryCatchPattern":"try { canonicalize(payload); } catch (e) { if (e.message === 'non-finite number in payload') { payload = sanitize(payload); canonicalize(payload); } else throw e; }","preventionTips":["Run Number.isFinite on computed metrics before adding them to facts","Never rely on JSON.stringify to mask NaN/Infinity","Represent unknown numerics as null or strings"],"tags":["json","serialization","signing"],"backgroundTag":"json-serialization-failed","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}