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
- Pass --facts-version <positive integer>, e.g. --facts-version 4
- Add a numeric facts_version field to the source JSON before signing
- Inspect the source JSON: Number(null) is 0 and Number(undefined) is NaN, both rejected
- 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
- Always keep facts_version as a positive integer in the source JSON
- Pass --facts-version explicitly in automation instead of relying on the source file
- Validate the source JSON before signing
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
- A positive pull request number is required
- API key contains invalid control characters
- API key id must be lowercase hex characters — the part…
- API key input is unexpectedly large
- API key must be - UTF-8 bytes
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)