Hmbown/CodeWhale · error · Error
invalid signed payload metadata
Error message
invalid signed payload metadata
What it means
After building the payload object, buildPayload re-validates it and additionally checks the envelope metadata: publishedAt must parse as a UTC ISO timestamp (utcTime), factsVersion must be a positive safe integer, and channel must match CHANNEL_RE. If any of these fail it throws this error, ensuring only well-formed metadata is ever signed.
Solutions
- Set publishedAt to a UTC ISO string like new Date().toISOString() that matches the utcTime regex
- Make factsVersion a positive safe integer (Number(...) and validate)
- Use a channel value matching the script's CHANNEL_RE (check the regex in facts-publish.mjs and match its exact format)
Example fix
// before
buildPayload(source, { channel: 'Prod', factsVersion: '12', publishedAt: '2024-01-01' });
// after
buildPayload(source, { channel: 'prod', factsVersion: 12, publishedAt: new Date().toISOString() }); Defensive patterns
Strategy: validation
Validate before calling
const ok = utcTime(publishedAt) !== null && Number.isSafeInteger(factsVersion) && factsVersion > 0 && CHANNEL_RE.test(channel);
if (!ok) throw new Error('bad payload metadata'); Type guard
const isPositiveSafeInt = (v) => Number.isSafeInteger(v) && v > 0;
Try / catch
try { buildPayload(source, meta); } catch (e) { if (e.message === 'invalid signed payload metadata') { console.error('check publishedAt (UTC ISO), factsVersion (positive int), channel format'); process.exitCode = 2; } else throw e; } Prevention
- Always derive publishedAt from new Date().toISOString()
- Coerce and check factsVersion with Number.isSafeInteger before passing
- Validate channel names against CHANNEL_RE in CI
When it happens
Trigger: Passing publishedAt like '2024-01-01' (no time/Z) or a non-UTC offset, factsVersion as 0, negative, a float, or a string, or channel not matching the allowed pattern (e.g. 'Prod' vs lowercase channel names).
Common situations: Generating publishedAt with toISOString of an invalid date, reading factsVersion from config as a string, or typos/casing mistakes in the channel name.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- bad channel slug
- Cargo metadata dependencies for
- Facts must be scalar metadata, not content objects.
- fleet task ' ' coordination contracts must be one non-empty…
- fleet task ' ' metadata.coordination_contracts must be an…
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/d0649cb19af033b4.
Report an issue: GitHub.
Appendix: source
Thrown at web/scripts/facts-publish.mjs:290
/** Build the signed payload object (no signing) from a source file. */
export function buildPayload(source, { channel, factsVersion, publishedAt }) {
const errors = validateSource(source);
if (errors.length) throw new Error(`source invalid:\n - ${errors.join("\n - ")}`);
const payload = {
schema_version: SCHEMA_VERSION,
channel,
facts_version: factsVersion,
published_at: publishedAt,
applies_to: typeof source.applies_to === "string" ? source.applies_to.trim() : "*",
models: source.models ?? [],
provider_defaults: source.provider_defaults ?? {},
release: source.release ?? null,
announcements: source.announcements ?? [],
};
if (source.not_after) payload.not_after = source.not_after;
const payloadErrors = validateSource(payload);
if (payloadErrors.length || utcTime(publishedAt) === null || !Number.isSafeInteger(factsVersion) || factsVersion <= 0 || !CHANNEL_RE.test(channel)) {
throw new Error("invalid signed payload metadata");
}
return payload;
}
export function buildEnvelope({ privateKey, keyId, payload }) {
if (!KEY_ID_RE.test(keyId)) throw new Error(`key_id must match ${KEY_ID_RE}`);
const payloadBytes = Buffer.from(canonicalize(payload), "utf8");
if (payloadBytes.length > MAX_PAYLOAD_BYTES) throw new Error(`payload exceeds ${MAX_PAYLOAD_BYTES} bytes`);
const sig = signPayload(privateKey, keyId, payloadBytes);
const sha256 = createHash("sha256").update(payloadBytes).digest("hex");
const envelope = {
envelope: ENVELOPE_VERSION,
channel: payload.channel,
facts_version: payload.facts_version,
schema_version: payload.schema_version,
key_id: keyId,
alg: "ed25519",
applies_to: payload.applies_to,View on GitHub (pinned to 433685b202)