denoland/deno · error · TypeError
Dispatcher connect.ca must be a string, Buffer, or ArrayBuff
Error message
Dispatcher connect.ca must be a string, Buffer, or ArrayBuffer
What it means
In Deno's bundled undici, dispatcher/Agent options.connect.ca is normalized by normalizeCaCerts: each entry must be a PEM string, an ArrayBuffer view (Buffer/Uint8Array), or an ArrayBuffer; a non-array value is wrapped into a list automatically. Any other type (X509Certificate, KeyObject, number, plain object, URL) throws this TypeError - note it has no code property, unlike the node: errors.
Source
Thrown at ext/node/polyfills/internal/deps/undici/undici.js:39
"Deno.internal.node.undici.dispatcherOptions",
);
const kGlobalDispatcher = SymbolFor(
"Deno.internal.node.undici.globalDispatcher",
);
function normalizeCaCerts(ca) {
const certs = ArrayIsArray(ca) ? ca : [ca];
return ArrayPrototypeMap(certs, (cert) => {
if (typeof cert === "string") {
return cert;
}
if (isArrayBufferView(cert)) {
return new TextDecoder().decode(cert);
}
if (isAnyArrayBuffer(cert)) {
return new TextDecoder().decode(new Uint8Array(cert));
}
throw new TypeError(
"Dispatcher connect.ca must be a string, Buffer, or ArrayBuffer",
);
});
}
class Agent {
constructor(options = { __proto__: null }) {
const connect = options.connect ?? { __proto__: null };
const dispatcherOptions = { __proto__: null };
if (connect.rejectUnauthorized === false) {
dispatcherOptions.unsafelyIgnoreCertificateErrors = true;
}
if (connect.ca !== undefined) {
dispatcherOptions.caCerts = normalizeCaCerts(connect.ca);
}
View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pass the PEM string or Buffer directly: connect: { ca: pem } (single values are fine - non-arrays are wrapped).
- If you hold an X509Certificate, unwrap it: connect: { ca: cert.toString() } (toString returns the PEM).
- Sanitize config before constructing the Agent: keep only string/Buffer entries and drop the rest.
Example fix
// before
const agent = new Agent({ connect: { ca: new X509Certificate(pem) } });
// after
const agent = new Agent({ connect: { ca: pem } }); // PEM string or Buffer
// or, from an X509Certificate: connect: { ca: cert.toString() } Defensive patterns
Strategy: validation
Validate before calling
function normalizeCa(ca) {
const list = Array.isArray(ca) ? ca : [ca];
return list.filter((c) =>
typeof c === 'string' || ArrayBuffer.isView(c) || c instanceof ArrayBuffer
);
}
const agent = new Agent({ connect: { ca: normalizeCa(config.ca) } }); Type guard
const isCaEntry = (v) => typeof v === 'string' || ArrayBuffer.isView(v) || v instanceof ArrayBuffer;
Try / catch
try {
new Agent({ connect: { ca } });
} catch (err) {
if (err instanceof TypeError && /connect\.ca/.test(err.message)) {
agent = new Agent({ connect: { ca: pemString } }); // fall back to raw PEM
} else {
throw err;
}
} Prevention
- Store CAs as PEM strings in config and convert anything else at the edge.
- Unwrap X509Certificate values with toString() before passing to undici connect options.
- Unit-test config parsing so ca entries are always string, Buffer or ArrayBuffer.
When it happens
Trigger: new Agent({ connect: { ca: new X509Certificate(pem) } }); ca: 123 from bad config; an array containing null or objects parsed from JSON; passing a Node https.Agent-style options object wholesale as connect options.
Common situations: Corporate MITM proxy setups injecting custom CAs; migrating Node fetch/undici configuration where ca shapes differ; config-driven HTTP clients where the CA field is optional and sometimes an object.
Related errors
- Unsupported transport: '${transport}'
- If "keyFormat" is specified, it must be "pem": received "${k
- If `cert` is specified, `key` must be specified as well for
- If `key` is specified, `cert` must be specified as well for
- A key and certificate are required for `Deno.listenTls`
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/d65add2699b7289e.
Report an issue: GitHub.