Hmbown/CodeWhale · error · Error

cannot parse exactly one TypeScript TRUSTED_KEYS table

Error message

cannot parse exactly one TypeScript TRUSTED_KEYS table

What it means

parseTsKeys extracts the pinned TRUSTED_KEYS table from web/lib/cloud-facts/keys.ts using a deliberately narrow regex over the source. It requires exactly one match for the `export const TRUSTED_KEYS: readonly TrustedKey[] = [...]` declaration; if zero or multiple tables are found the gate fails closed so a drifted or renamed key table cannot be silently ignored.

Solutions

  1. Open web/lib/cloud-facts/keys.ts and ensure there is exactly one `export const TRUSTED_KEYS: readonly TrustedKey[] = [ ... ];` declaration, matching that exact type annotation
  2. Remove any duplicate or commented-out second declaration of TRUSTED_KEYS from the file
  3. If the key-table format intentionally changed, update parseTsKeys and its tests together rather than loosening the regex silently

Example fix

// before (keys.ts)
export const TRUSTED_KEYS: TrustedKey[] = [ ... ];
// after
export const TRUSTED_KEYS: readonly TrustedKey[] = [ ... ];
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from "node:fs";
const text = readFileSync("web/lib/cloud-facts/keys.ts", "utf8");
const matches = [...text.matchAll(/export\s+const\s+TRUSTED_KEYS\s*:/g)];
if (matches.length !== 1) throw new Error(`expected exactly one TRUSTED_KEYS declaration, found ${matches.length}`);

Try / catch

try {
  const keys = parseTsKeys(text);
} catch (err) {
  if (err.message.includes("exactly one TypeScript TRUSTED_KEYS")) {
    console.error("keys.ts table declaration missing, duplicated, or reformatted — fix web/lib/cloud-facts/keys.ts");
    process.exit(1);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling parseTsKeys on file text where (a) the TRUSTED_KEYS declaration is missing/renamed, (b) its type annotation no longer reads exactly `: readonly TrustedKey[]`, (c) it was changed to a non-array or moved into a comment (comments are stripped first), or (d) the file accidentally declares the table twice.

Common situations: A refactor renamed TRUSTED_KEYS or changed its type annotation; a merge duplicated the const; someone reformatted the declaration with a line break inside the type annotation; the keys file was replaced wholesale with a JSON file or different structure.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

  const key = createPrivateKey({ key: pem, format: "pem" });
  if (key.asymmetricKeyType !== "ed25519") throw new Error("signing key must be Ed25519");
  return key;
}

export function validateTrustedKeys(keys) {
  const seen = new Set();
  for (const key of keys) {
    if (!KEY_ID_RE.test(key.keyId) || seen.has(key.keyId) || !["active", "retired"].includes(key.status) || strictBase64(key.publicKey, 32).length !== 32) throw new Error("invalid or duplicated pinned key");
    seen.add(key.keyId);
  }
  return keys;
}

/** Deliberately narrow syntax: a changed/unparseable table must fail the gate. */
export function parseTsKeys(text) {
  const source = text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
  const tables = [...source.matchAll(/^\s*export\s+const\s+TRUSTED_KEYS\s*:\s*readonly\s+TrustedKey\[\]\s*=\s*\[([\s\S]*?)\]\s*;/gm)];
  if (tables.length !== 1) throw new Error("cannot parse exactly one TypeScript TRUSTED_KEYS table");
  const table = tables[0];
  const body = table[1].replace(/^\s*\/\/.*$/gm, "");
  const keys = [];
  const remainder = body.replace(/\{\s*keyId:\s*"([^"]+)",\s*publicKey:\s*"([^"]+)",\s*status:\s*"([^"]+)"\s*,?\s*\}/g, (_, keyId, publicKey, status) => {
    keys.push({ keyId, publicKey, status });
    return "";
  });
  if (remainder.replace(/[\s,]/g, "")) throw new Error("unparsed TypeScript TRUSTED_KEYS entry");
  return validateTrustedKeys(keys);
}

function loadTrustedKeysFromRepo() {
  const keys = parseTsKeys(readBoundedFile(resolve(WEB_ROOT, "lib/cloud-facts/keys.ts"), 64 * 1024).toString("utf8"));
  return new Map(keys.map((key) => [key.keyId, key]));
}

export function activePublishingKey(envelope, keys, now = Date.now()) {
  const key = validateTrustedKeys(keys).find((key) => key.keyId === envelope.key_id && key.status === "active");

View on GitHub (pinned to 433685b202)