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

  1. Print the keyId and match it against KEY_ID_RE (imported from this module) to see which character violates it
  2. Trim whitespace/newlines from the keyId before passing it
  3. Use the exact key id recorded in the trusted-keys pin list (validateTrustedKeys accepts the same pattern)
  4. 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

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


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)