Hmbown/CodeWhale · error · Error
key_id must match
Error message
key_id must match ${KEY_ID_RE} What it means
buildEnvelope signs a facts payload and wraps it in a signed envelope. Before doing any work it validates the keyId against the repository's KEY_ID_RE regular expression and throws this error when the identifier does not conform to the pinned key-id syntax. Key ids must be canonical so verifiers can match them against pinned trusted keys.
Solutions
- Print the keyId and match it against KEY_ID_RE (imported from this module) to see which character violates it
- Trim whitespace/newlines from the keyId before passing it
- Use the exact key id recorded in the trusted-keys pin list (validateTrustedKeys accepts the same pattern)
- If the id is legacy, re-pin the key under a conforming id and republish
Example fix
// before
await buildEnvelope({ privateKey, keyId: 'my signing key', payload });
// after
const keyId = process.env.CODEWHALE_FACTS_KEY_ID.trim();
if (!KEY_ID_RE.test(keyId)) throw new Error(`bad key id: ${JSON.stringify(keyId)}`);
await buildEnvelope({ privateKey, keyId, payload }); Defensive patterns
Strategy: validation
Validate before calling
import { KEY_ID_RE } from './facts-publish.mjs';
if (typeof keyId !== 'string' || !KEY_ID_RE.test(keyId)) throw new Error(`keyId must match ${KEY_ID_RE}: got ${JSON.stringify(keyId)}`); Type guard
const isValidKeyId = (k) => typeof k === 'string' && KEY_ID_RE.test(k);
Try / catch
try { env = buildEnvelope({ privateKey, keyId, payload }); } catch (e) { if (String(e.message).startsWith('key_id must match')) { console.error('Bad key id:', JSON.stringify(keyId)); process.exit(2); } throw e; } Prevention
- Trim and normalize key ids read from env/files before use
- Store key ids only in one canonical place (the pin list) and reference them
- Add a startup assertion that all configured ids match KEY_ID_RE
- Never hand-type key ids; copy from validated config
When it happens
Trigger: Calling buildEnvelope({ privateKey, keyId, payload }) with a keyId that fails KEY_ID_RE.test(keyId) — e.g. empty string, contains illegal characters, wrong case, or extra whitespace.
Common situations: Passing a human-friendly key label ('my key') or a path-like id instead of the canonical id used when the key was pinned; copy-pasting a key id with trailing newline or spaces; upgrading the KEY_ID_RE pattern so previously-valid ids no longer match.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- bad channel slug
- invalid signed payload metadata
- source invalid:\n
- 1
- A pinned task provider requires an explicit model
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/fc4c6326e2e86ab4.
Report an issue: GitHub.
Appendix: source
Thrown at web/scripts/facts-publish.mjs:296
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,
published_at: payload.published_at,
payload_b64: payloadBytes.toString("base64"),
sig_b64: sig.toString("base64"),
sigs: [],
sha256,
};View on GitHub (pinned to 433685b202)