Hmbown/CodeWhale · error · Error

--facts-version (or source.facts_version) must be a…

Error message

--facts-version (or source.facts_version) must be a positive integer

What it means

The sign subcommand takes the facts version from --facts-version or source.facts_version, coerces it with Number(), and requires a safe positive integer. Non-numeric strings, floats, zero, negatives, NaN and unsafe integers all throw this error.

Solutions

  1. Pass --facts-version <positive integer>, e.g. --facts-version 4
  2. Add a numeric facts_version field to the source JSON before signing
  3. Inspect the source JSON: Number(null) is 0 and Number(undefined) is NaN, both rejected
  4. Ensure no shell quoting turns the value into text with stray characters

Example fix

// before
node facts-publish.mjs sign envelope.json --facts-version 2.5
// after
node facts-publish.mjs sign envelope.json --facts-version 3
Defensive patterns

Strategy: validation

Validate before calling

const v = Number(flags['facts-version'] ?? source.facts_version);
if (!Number.isSafeInteger(v) || v <= 0) throw new Error(`facts_version must be a positive integer, got: ${v}`);

Try / catch

try {
  await run(['sign', srcPath, '--facts-version', String(v)]);
} catch (e) {
  if (e.message.includes('positive integer')) console.error('Pass an integer >= 1 via --facts-version or set facts_version in the source JSON');
  throw e;
}

Prevention

When it happens

Trigger: `sign --facts-version abc` (NaN), --facts-version 1.5, --facts-version 0 or -1, or a source JSON with facts_version missing, null, or a non-integer like "3" only if Number coercion fails — note "3" passes, but an omitted flag plus missing source field yields NaN.

Common situations: A freshly created docs/cloud-facts/stable.json missing facts_version; a flag pasted with units or a trailing period ("2."); automation writing the version as a non-integer or leaving it empty.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

    const raw = rawPublicKeyFromKeyObject(publicKey);
    console.log(JSON.stringify({
      key_id: keyId,
      algorithm: "ed25519",
      public_key_b64: raw.toString("base64"),
      public_key_bytes: [...raw],
      private_key_file: out,
      note: "Private key written with mode 0600. Move it into custody (password manager); never commit it.",
    }, null, 2));
    return 0;
  }
  if (cmd === "sign") {
    refuseUnderCi();
    const sourcePath = resolve(String(flags.source ?? resolve(REPO_ROOT, "docs/cloud-facts/stable.json")));
    const source = readJson(sourcePath);
    const channel = String(flags.channel ?? source.channel ?? "stable");
    if (!CHANNEL_RE.test(channel)) throw new Error("bad channel slug");
    const factsVersion = Number(flags["facts-version"] ?? source.facts_version);
    if (!Number.isSafeInteger(factsVersion) || factsVersion <= 0) throw new Error("--facts-version (or source.facts_version) must be a positive integer");
    const keyId = String(flags["key-id"] ?? "");
    const privateKey = loadPrivateKeyFromEnv();
    const publishedAt = String(flags["published-at"] ?? nowIso());
    const payload = buildPayload(source, { channel, factsVersion, publishedAt });
    const envelope = buildEnvelope({ privateKey, keyId, payload });
    const text = `${JSON.stringify(envelope, null, 2)}\n`;
    if (flags.out) {
      writeFileSync(resolve(String(flags.out)), text);
      console.error(`wrote ${flags.out} (channel=${channel} facts_version=${factsVersion} key_id=${keyId} sha256=${envelope.sha256})`);
    } else process.stdout.write(text);
    return 0;
  }
  if (cmd === "verify") {
    const envelope = readJson(resolve(String(positional[1] ?? "")));
    let pub = flags["public-key"];
    if (!pub) {
      const trusted = loadTrustedKeysFromRepo().get(envelope.key_id);
      if (!trusted || trusted.status !== "active") throw new Error("key is not pinned and active; use --public-key only for explicit offline verification");

View on GitHub (pinned to 433685b202)