Hmbown/CodeWhale · error · Error
unparsed Rust TRUSTED_KEYS entry
Error message
unparsed Rust TRUSTED_KEYS entry
What it means
After regex-extracting all TrustedKey entries from the table body, the checker verifies nothing remains except whitespace and commas. If leftover non-whitespace text exists, the table contained a construct the parser does not understand, and it throws rather than silently skipping keys — a security-relevant check since an unparsed key would be invisible to the comparison.
Solutions
- Look at what text remains unmatched — the error gives no offset, so diff the table against the regex: TrustedKey { key_id: "..", public_key: [..], status: KeyStatus::Active|Retired }.
- Update the parser regex if TrustedKey gained legitimate fields.
- Use only Active or Retired statuses in the table, or extend the alternation.
- Remove non-TrustedKey items or attributes from the array.
Example fix
// before (Rust)
#[allow(dead_code)]
TrustedKey { key_id: "old", ... },
// after
TrustedKey { key_id: "old", ... }, Defensive patterns
Strategy: validation
Validate before calling
const stripped = tableBody.replace(/TrustedKey\s*\{[^}]*\}/g, "").replace(/[\s,]/g, ""); if (stripped) throw new Error("unrecognized content in TRUSTED_KEYS: " + stripped.slice(0, 80)); Try / catch
try { parseRustKeys(src); } catch (e) { if (e.message.includes("unparsed")) console.error("TRUSTED_KEYS contains a construct the parser does not know"); throw e; } Prevention
- Extend the parser regex whenever TrustedKey gains fields.
- Restrict statuses to Active|Retired or update the alternation.
- Keep attributes and foreign items out of the table.
- Run the checker in CI on every change to the keys module.
When it happens
Trigger: A TrustedKey entry formatted differently than the regex expects (extra fields, reordered fields, missing status, different brace style), a deprecated/retired key marked with an attribute like #[deprecated] or #[allow(...)] inside the table, or a non-TrustedKey item in the array.
Common situations: Adding a new field to TrustedKey (e.g. added_at) without updating the checker; using `KeyStatus::Revoked` (not Active|Retired) so the status alternation fails; multi-line formatting with attributes between entries.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Failed to update setting: invalid permission posture
- Rust public key byte out of range
- Rust public key must contain 32 literal bytes
- A pinned task provider requires an explicit model
- agent profile provider cannot be empty
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/4182abc6b0c264a3.
Report an issue: GitHub.
Appendix: source
Thrown at web/scripts/check-cloud-facts.mjs:26
const REPO_ROOT = resolve(WEB_ROOT, "..");
export { parseTsKeys };
export function parseRustKeys(text) {
const source = text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
const tables = [...source.matchAll(/^\s*pub\s+const\s+TRUSTED_KEYS\s*:\s*&\s*\[TrustedKey\]\s*=\s*&\s*\[([\s\S]*?)\]\s*;/gm)];
if (tables.length !== 1) throw new Error("cannot parse exactly one Rust TRUSTED_KEYS table");
const table = tables[0];
const body = table[1].replace(/^\s*\/\/.*$/gm, "");
const keys = [];
const remainder = body.replace(/TrustedKey\s*\{\s*key_id:\s*"([^"]+)",\s*public_key:\s*\[([^\]]*)\],\s*status:\s*KeyStatus::(Active|Retired)\s*,?\s*\}/g, (_, keyId, encoded, status) => {
const pieces = encoded.split(",").map((piece) => piece.trim()).filter(Boolean);
if (pieces.length !== 32 || pieces.some((piece) => !/^(?:\d+|0x[0-9a-fA-F]+)$/.test(piece))) throw new Error("Rust public key must contain 32 literal bytes");
const bytes = pieces.map(Number);
if (bytes.some((byte) => !Number.isInteger(byte) || byte < 0 || byte > 255)) throw new Error("Rust public key byte out of range");
keys.push({ keyId, publicKey: Buffer.from(bytes).toString("base64"), status: status.toLowerCase() });
return "";
});
if (remainder.replace(/[\s,]/g, "")) throw new Error("unparsed Rust TRUSTED_KEYS entry");
return validateTrustedKeys(keys);
}
function text(path) { return readBoundedFile(path).toString("utf8"); }
function json(path) { return JSON.parse(text(path)); }
export function checkCloudFacts() {
const failures = [];
const source = json(resolve(REPO_ROOT, "docs/cloud-facts/stable.json"));
for (const error of validateSource(source)) failures.push(`stable.json: ${error}`);
if (source.channel !== "stable") failures.push("stable.json: channel must be stable");
const latest = json(resolve(WEB_ROOT, "data/latest-published-release.json"));
if (source.release?.latest !== latest.version) failures.push("stable.json release.latest differs from latest-published-release.json");
if (source.release?.release_url && source.release.release_url !== latest.url) failures.push("stable.json release.release_url differs from latest-published-release.json");
// An explicit empty table is valid and inert; parse failures are never empty.
const rustKeys = parseRustKeys(text(resolve(REPO_ROOT, "crates/config/src/cloud_facts/keys.rs")));
const tsKeys = parseTsKeys(text(resolve(WEB_ROOT, "lib/cloud-facts/keys.ts")));
if (JSON.stringify(rustKeys) !== JSON.stringify(tsKeys)) failures.push("Rust and web pinned key tables diverge");View on GitHub (pinned to 433685b202)