Hmbown/CodeWhale · error · Error
signing key exceeds size limit
Error message
signing key exceeds size limit
What it means
After loading the PEM (from env or via readBoundedFile capped at 16 KiB), loadPrivateKeyFromEnv re-checks the byte length and throws if it exceeds 16 KiB. The bound is a defensive limit so an accidentally huge or hostile env value cannot be parsed as a key.
Solutions
- Trim the env value to a single PEM block (one BEGIN/END PRIVATE KEY section)
- Check size: printenv CODEWHALE_FACTS_SIGNING_KEY | wc -c — keep it well under 16384
- If using the file path variant, ensure the file holds only one key
- Regenerate a fresh Ed25519 key if yours was wrapped with unusual armor padding
Example fix
// before
export CODEWHALE_FACTS_SIGNING_KEY="$(cat bundle.pem)" # several keys
// after
awk '/BEGIN/{f=1} f{print} /END/{exit}' bundle.pem > one.pem
export CODEWHALE_FACTS_SIGNING_KEY="$(cat one.pem)" Defensive patterns
Strategy: validation
Validate before calling
const pem = process.env.CODEWHALE_FACTS_SIGNING_KEY ?? '';
if (Buffer.byteLength(pem) > 16 * 1024) throw new Error(`signing key is ${Buffer.byteLength(pem)}B; must be under 16384B`); Type guard
const isBoundedPem = (s) => typeof s === 'string' && s.length > 0 && Buffer.byteLength(s) <= 16 * 1024;
Try / catch
try { key = loadPrivateKeyFromEnv(); } catch (e) { if (e.message === 'signing key exceeds size limit') { console.error('Trim the PEM to a single key block under 16 KiB'); process.exit(2); } throw e; } Prevention
- Export exactly one PEM block — never whole bundles
- Check `printenv ... | wc -c` after setting the variable
- Watch for shell template expansion duplicating content
- Regenerate keys with standard armor if the PEM is unusually padded
When it happens
Trigger: loadPrivateKeyFromEnv when Buffer.byteLength(pem) > 16384 — e.g. CODEWHALE_FACTS_SIGNING_KEY containing a multi-key bundle, embedded newlines/whitespace bloat, or pasted extra content.
Common situations: Exporting the whole ~/.ssh output or a concatenated authority bundle into the env var; shell quoting duplicating content; a secrets template expanding to multiple keys.
Understand the failure class
Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.
Related errors
- Pet bucket exceeds the recording segment byte limit.
- set CODEWHALE_FACTS_SIGNING_KEY (PEM) or…
- signing key must be Ed25519
- 127
- A pinned task provider requires an explicit model
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/67093604285778d4.
Report an issue: GitHub.
Appendix: source
Thrown at web/scripts/facts-publish.mjs:367
const bytes = Buffer.alloc(maxBytes + 1);
let size = 0;
while (size <= maxBytes) {
const count = readSync(fd, bytes, size, maxBytes + 1 - size, null);
if (!count) break;
size += count;
}
if (size > maxBytes) throw new Error("file exceeds size limit");
return bytes.subarray(0, size);
} finally { closeSync(fd); }
}
function loadPrivateKeyFromEnv() {
refuseUnderCi();
let pem = process.env.CODEWHALE_FACTS_SIGNING_KEY;
const file = process.env.CODEWHALE_FACTS_SIGNING_KEY_FILE;
if (!pem && file) pem = readBoundedFile(file, 16 * 1024).toString("utf8");
if (!pem) throw new Error("set CODEWHALE_FACTS_SIGNING_KEY (PEM) or CODEWHALE_FACTS_SIGNING_KEY_FILE");
if (Buffer.byteLength(pem) > 16 * 1024) throw new Error("signing key exceeds size limit");
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)];View on GitHub (pinned to 433685b202)