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
- 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
- Remove any duplicate or commented-out second declaration of TRUSTED_KEYS from the file
- 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
- Never rename TRUSTED_KEYS or change its `readonly TrustedKey[]` type annotation without updating the publish script
- Keep exactly one declaration; delete commented-out copies of the table
- Run the publish script's parse step in CI (with publishing disabled) to catch drift early
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
- unparsed TypeScript TRUSTED_KEYS entry
- Duplicate .
- Invalid Engine pet metadata.
- Invalid Engine pet metadata fields.
- Invalid fact key: nonempty bounded text required.
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)