sindresorhus/got · error · TypeError
The `${key}` header must be a single value
Error message
The `${key}` header must be a single value What it means
Thrown at source/core/index.ts:2190 in `sanitizeHeaders`. The `transfer-encoding` header controls HTTP message framing, and Node serializes header arrays as repeated field lines. To keep framing unambiguous, got insists that transfer-encoding be a single string value, not an array. If you pass `transfer-encoding: ['chunked', 'gzip']` (or any array whose length isn't exactly 1), got throws. The single-element array case is permitted and normalized down to a string.
Source
Thrown at source/core/index.ts:2190
}
};
const getAuthorizationHeader = (username: string, password: string, isExplicitlyOmitted: boolean) => !isExplicitlyOmitted && (username || password)
? `Basic ${stringToBase64(`${username}:${password}`)}`
: undefined;
const sanitizeHeaders = () => {
const currentHeaders = options.getInternalHeaders();
for (const key in currentHeaders) {
if (is.undefined(currentHeaders[key])) {
options.deleteInternalHeader(key);
} else if (is.null(currentHeaders[key])) {
throw new TypeError(`Use \`undefined\` instead of \`null\` to delete the \`${key}\` header`);
} else if (Array.isArray(currentHeaders[key]) && key === 'transfer-encoding') {
// Node serializes request header arrays as repeated field lines. Keep framing
// unambiguous by allowing only one transfer-encoding value here.
if (currentHeaders[key].length !== 1) {
throw new TypeError(`The \`${key}\` header must be a single value`);
}
options.setInternalHeader(key, currentHeaders[key][0]);
} else if (Array.isArray(currentHeaders[key]) && singleValueRequestHeaders.has(key)) {
// Duplicate credential and content-length lines are not allowed on requests.
// Normalize a single-element array to match the long-supported string path.
if (currentHeaders[key].length !== 1) {
throw new TypeError(`The \`${key}\` header must be a single value`);
}
options.setInternalHeader(key, currentHeaders[key][0]);
}
}
return currentHeaders;
};
const getCookieHeader = async (cookieJar: PromiseCookieJar | undefined) => {View on GitHub (pinned to e3924aa1e5)
Solutions
- Pass transfer-encoding as a single string: `'chunked'` or `'chunked, gzip'` if you genuinely need stacked encodings.
- Normalize header values before passing to got: if a value is an array of length 1, unwrap it; if length > 1 for transfer-encoding, join with ', '.
- Let got set transfer-encoding automatically based on the body — do not set it manually for streamed bodies.
Example fix
// before
await got.post(url, { headers: { 'transfer-encoding': ['chunked', 'gzip'] }, body: stream });
// after — single value
await got.post(url, { headers: { 'transfer-encoding': 'chunked, gzip' }, body: stream }); Defensive patterns
Strategy: validation
Validate before calling
// Enforce single-value framing headers before the call.
function normalizeFramingHeaders(headers) {
const FRAMING = new Set(['transfer-encoding']);
for (const [k, v] of Object.entries(headers)) {
if (FRAMING.has(k.toLowerCase()) && Array.isArray(v)) {
if (v.length === 0) delete headers[k];
else if (v.length === 1) headers[k] = v[0];
else throw new TypeError(`The \`${k}\` header must be a single value`);
}
}
return headers;
}
await got(url, { headers: normalizeFramingHeaders(headers) }); Type guard
function isSingleValueHeader(key: string, value: unknown): boolean {
if (!Array.isArray(value)) return true;
return value.length === 1;
} Try / catch
try {
await got(url, { headers });
} catch (error) {
if (error instanceof TypeError && /transfer-encoding.*single value/.test(error.message)) {
const fixed = { ...headers, 'transfer-encoding': Array.isArray(headers['transfer-encoding']) ? headers['transfer-encoding'].join(', ') : headers['transfer-encoding'] };
return got(url, { headers: fixed });
}
throw error;
} Prevention
- Pass transfer-encoding as a single string ('chunked' or 'chunked, gzip').
- Let got derive transfer-encoding from the body; don't set it manually for streamed bodies.
- Normalize header values before the call: unwrap single-element arrays, join multi-element arrays.
When it happens
Trigger: Passing `headers: { 'transfer-encoding': ['chunked', 'gzip'] }` (stacked encodings as an array); producing headers dynamically and accidentally wrapping transfer-encoding in an array; copy-pasting server-side header handling that tolerates arrays.
Common situations: Custom serialization layers that wrap all headers in arrays for uniformity; proxy-style code forwarding arbitrary header arrays; misconfigured middleware that adds an encoding to an existing array.
Related errors
- Use `undefined` instead of `null` to delete the `${key}` hea
- HTTP/2 pseudo-headers are not supported in `options.headers`
- Missing `url` property
- The `${options.method}` method cannot be used with a body
- Unexpected option: ${key}
AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03).
Data as JSON: /data/errors/241f7a8085c0b25d.json.
Report an issue: GitHub.