Hmbown/CodeWhale · error · Error

non-finite number in payload

Error message

non-finite number in payload

What it means

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.

Solutions

  1. Sanitize the payload before canonicalizing: replace or remove non-finite numbers
  2. Use Number.isFinite() on every numeric field before building the payload
  3. Store such values as strings or null explicitly if they must appear

Example fix

// before
const facts = { uptime: 1 / 0 };
canonicalize(facts);
// after
const facts = { uptime: Number.isFinite(raw) ? raw : null };
canonicalize(facts);
Defensive patterns

Strategy: validation

Validate before calling

function assertFiniteNumbers(obj) {
  for (const [k, v] of Object.entries(obj)) {
    if (typeof v === 'number' && !Number.isFinite(v)) throw new Error(`${k} is not finite`);
    if (v && typeof v === 'object') assertFiniteNumbers(v);
  }
}
assertFiniteNumbers(payload);

Type guard

const isFiniteNumber = (v) => typeof v === 'number' && Number.isFinite(v);

Try / catch

try { canonicalize(payload); } catch (e) { if (e.message === 'non-finite number in payload') { payload = sanitize(payload); canonicalize(payload); } else throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: Building facts from metrics computed with parseFloat('x'), 0/0, or Math.log(-1); JSON.stringify silently writes null but this canonicalizer refuses.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/c2ccaf2fed26aa48. Report an issue: GitHub.

Appendix: source

Thrown at web/scripts/facts-publish.mjs:51

export const SCHEMA_VERSION = 1;
export const MAX_PAYLOAD_BYTES = 512 * 1024;
export const MAX_ENVELOPE_BYTES = 768 * 1024;
const KEY_ID_RE = /^cwf-[a-z0-9-]{1,32}$/;
const CHANNEL_RE = /^[a-z0-9][a-z0-9-]{0,31}$/;
const CI_MARKERS = ["CI", "GITHUB_ACTIONS", "GITLAB_CI", "BUILDKITE", "CIRCLECI", "JENKINS_URL", "TF_BUILD"];

const here = dirname(fileURLToPath(import.meta.url));
const WEB_ROOT = resolve(here, "..");
const REPO_ROOT = resolve(WEB_ROOT, "..");

// ---------------------------------------------------------------------------
// Canonical JSON + signing primitives (exported for tests)
// ---------------------------------------------------------------------------

export function canonicalize(value) {
  if (value === null || typeof value !== "object") {
    if (typeof value === "number" && !Number.isFinite(value)) {
      throw new Error("non-finite number in payload");
    }
    return JSON.stringify(value);
  }
  if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
  const keys = Object.keys(value).sort();
  const parts = [];
  for (const key of keys) {
    const v = value[key];
    if (v === undefined) continue;
    parts.push(`${JSON.stringify(key)}:${canonicalize(v)}`);
  }
  return `{${parts.join(",")}}`;
}

export function signingMessage(keyId, payloadBytes) {
  return Buffer.concat([Buffer.from(DOMAIN, "utf8"), Buffer.from(keyId, "utf8"), Buffer.from([0]), payloadBytes]);
}

View on GitHub (pinned to 433685b202)