oven-sh/bun · critical
InvalidCA
Error message
InvalidCA
What it means
An inline CA certificate (passed as cert data rather than a file path — uws invalid_ca mapped at src/http/HTTPContext.rs:520; message 'the provided CA is invalid' at src/http/HTTPThread.rs:369-371) failed to parse. This covers CA material supplied through APIs that feed BunSocketContextOptions.ca (inline PEM strings in fetch/Bun.connect-style TLS options or install config `ca` entries).
Source
Thrown at src/http/InitError.rs:9
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error, strum::IntoStaticStr)]
pub enum InitError {
#[error("FailedToOpenSocket")]
FailedToOpenSocket,
#[error("LoadCAFile")]
LoadCAFile,
#[error("InvalidCAFile")]
InvalidCAFile,
#[error("InvalidCA")]
InvalidCA,
#[error("InvalidCRL")]
InvalidCRL,
}
View on GitHub (pinned to 8c5296ac45)
Solutions
- Round-trip the inline CA through a parser before handing it to Bun: `Bun.file`-free check with `openssl x509 -noout -subject` via stdin, or in JS verify it starts with '-----BEGIN CERTIFICATE-----'.
- Fix newline handling: replace literal '\\n' sequences with '\n' and ensure LF endings.
- Prefer `cafile` with a verified PEM on disk over inline strings when possible — it gets clearer diagnostics (LoadCAFile vs InvalidCAFile).
- Make sure you pass the root/intermediate CA, not the server's leaf certificate.
Example fix
// before: secret stored single-line
const ca = process.env.CA_PEM; // "-----BEGIN CERTIFICATE-----\\nMIIF...\\n" with literal backslash-n
await fetch('https://internal', { tls: { ca } });
// after
const ca = process.env.CA_PEM!.replace(/\\n/g, '\n');
await fetch('https://internal', { tls: { ca } }); Defensive patterns
Strategy: validation
Validate before calling
function isPemCert(s) {
return typeof s === 'string' &&
s.startsWith('-----BEGIN CERTIFICATE-----') &&
s.includes('-----END CERTIFICATE-----') &&
!s.includes('PRIVATE KEY');
}
const ca = process.env.CA_PEM!.replace(/\\n/g, '\n');
if (!isPemCert(ca)) throw new Error('inline CA is not a PEM certificate');
await fetch('https://internal', { tls: { ca } }); Type guard
function isPemCert(value) {
return typeof value === 'string' &&
/^-----BEGIN CERTIFICATE-----[\s\S]+-----END CERTIFICATE-----/.test(value);
} Prevention
- Normalize literal '\\n' to real newlines when CAs come from env/secrets
- Pass root/intermediate CAs, never leaf certificates
- Prefer cafile over inline strings for clearer diagnostics
When it happens
Trigger: Passing an inline PEM string that is truncated, has escaped newlines mangled by JSON/env interpolation, uses CRLF line endings from a Windows secret, or contains a non-CA end-entity certificate as the trust anchor.
Common situations: Secrets managers storing the PEM as a single-line string with literal '\n' that never get converted to real newlines; env-var CA data base64'd once too many/few times; corporate MITM proxies documented to hand out leaf certs instead of the root.
Related errors
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/e6c1a0ad7df73247.
Report an issue: GitHub.