sindresorhus/got · error · ReadError

ERR_HTTP_CONTENT_LENGTH_MISMATCH

ERR_HTTP_CONTENT_LENGTH_MISMATCH

Error message

Content-Length mismatch: expected ${this._expectedContentLength} bytes, received ${actualSize} bytes

What it means

Produced at source/core/index.ts:900 by `_checkContentLengthMismatch`. When `strictContentLength` is true (the default) and the server sent a Content-Length header, got tracks the actual bytes received (`_compressedBytesCount` for compressed responses, `_downloadedSize` otherwise). If those numbers diverge, got emits a ReadError with code ERR_HTTP_CONTENT_LENGTH_MISMATCH. This mirrors Node's own validation: a mismatch means the response body was truncated or padded, so any parsed body is unreliable.

Source

Thrown at source/core/index.ts:900

		(this._requestOptions as (NativeRequestOptions & {_alpnSocket?: Socket}) | undefined)?._alpnSocket?.destroy();
	}

	private _shouldIncrementallyDecodeBody(): boolean {
		const {responseType, encoding} = this.options;

		return Boolean(this._noPipe)
			&& (responseType === 'text' || responseType === 'json')
			&& isUtf8Encoding(encoding)
			&& typeof globalThis.TextDecoder === 'function';
	}

	private _checkContentLengthMismatch(): boolean {
		if (this.options.strictContentLength && this._expectedContentLength !== undefined) {
			// Use compressed bytes count when available (for compressed responses),
			// otherwise use _downloadedSize (for uncompressed responses)
			const actualSize = this._compressedBytesCount ?? this._downloadedSize;
			if (actualSize !== this._expectedContentLength) {
				this._beforeError(new ReadError({
					message: `Content-Length mismatch: expected ${this._expectedContentLength} bytes, received ${actualSize} bytes`,
					name: 'Error',
					code: 'ERR_HTTP_CONTENT_LENGTH_MISMATCH',
				}, this));
				return true;
			}
		}

		return false;
	}

	private async _finalizeBody(): Promise<void> {
		const {options} = this;
		const headers = options.getInternalHeaders();

		const isForm = !is.undefined(options.form);
		// eslint-disable-next-line @typescript-eslint/naming-convention
		const isJSON = !is.undefined(options.json);

View on GitHub (pinned to e3924aa1e5)

Solutions

  1. Retry the request — the underlying cause is almost always a transient connection drop, and got's default retry will catch ECONNRESET but you may need to add ERR_HTTP_CONTENT_LENGTH_MISMATCH to retry.errorCodes.
  2. If you intentionally disable decompression (`decompress: false`), also set `strictContentLength: false` because the raw byte count will not match a Content-Length that describes the decompressed payload.
  3. Verify the upstream server/proxy is sending a correct Content-Length (or omitting it in favor of Transfer-Encoding: chunked) for compressed payloads.

Example fix

// before — strict content-length check on a compressed body you don't decompress
await got('https://api', { decompress: false });

// after — relax the check when intentionally reading raw bytes
await got('https://api', { decompress: false, strictContentLength: false });
Defensive patterns

Strategy: retry

Validate before calling

// Configure retry to cover the mismatch code, and relax strictContentLength when reading raw bytes.
const options = {
  strictContentLength: !decompressDisabled, // set false when decompress: false
  retry: { limit: 3, errorCodes: ['ERR_HTTP_CONTENT_LENGTH_MISMATCH', 'ECONNRESET'] }
};

Try / catch

import {HTTPError} from 'got';

try {
  await got(url, { retry: { limit: 3 } });
} catch (error) {
  if (error instanceof Error && (error as any).code === 'ERR_HTTP_CONTENT_LENGTH_MISMATCH') {
    // transient truncation — got may have already retried; surface to caller for decision
    throw new Error('Response body was truncated (Content-Length mismatch). Retry upstream.', { cause: error });
  }
  throw error;
}

Prevention

When it happens

Trigger: Server closes the connection mid-body (truncated response); proxy or CDN miscounts bytes; compression layer (gzip/br/zstd) sends a Content-Length that doesn't match the compressed payload; server streams chunks and mis-declares the total length; client uses `decompress: false` so the raw byte count is compared against a Content-Length that was meant for the decompressed body.

Common situations: Flaky upstream that drops connections, reverse proxies that rewrite bodies without updating Content-Length, Brotli/zstd-compressed responses behind an HTTP/1 proxy, or disabling decompression while strictContentLength is on. Also seen when a server uses chunked transfer-encoding but also sets Content-Length.

Related errors


AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03). Data as JSON: /data/errors/630fba689810138e.json. Report an issue: GitHub.