nodejs/node · error · Error

too many content-encodings in response: ${parts.length}, max

Error message

too many content-encodings in response: ${parts.length}, maximum allowed is ${maxContentEncodings}

What it means

Thrown by the decompress() interceptor's #createDecompressionChain when the response Content-Encoding header contains more than 5 comma-separated encodings. This is a deliberate resource-exhaustion guard mirroring fixes for urllib3 GHSA-gm62-xv2j-4w53 and curl CVE-2022-32206: deeply nested encodings can be used to amplify a small response into huge CPU/memory use, so the interceptor caps the chain length at 5.

Source

Thrown at deps/undici/src/lib/interceptor/decompress.js:75

    if (this.#skipErrorResponses && statusCode >= 400) return true
    return false
  }

  /**
   * Creates a chain of decompressors for multiple content encodings
   *
   * @param {string} encodings - Comma-separated list of content encodings
   * @returns {Array<DecompressorStream>} - Array of decompressor streams
   * @throws {Error} - If the number of content-encodings exceeds the maximum allowed
   */
  #createDecompressionChain (encodings) {
    const parts = encodings.split(',')

    // Limit the number of content-encodings to prevent resource exhaustion.
    // CVE fix similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206).
    const maxContentEncodings = 5
    if (parts.length > maxContentEncodings) {
      throw new Error(`too many content-encodings in response: ${parts.length}, maximum allowed is ${maxContentEncodings}`)
    }

    /** @type {DecompressorStream[]} */
    const decompressors = []

    for (let i = parts.length - 1; i >= 0; i--) {
      const encoding = parts[i].trim()
      if (!encoding) continue

      if (!supportedEncodings[encoding]) {
        decompressors.length = 0 // Clear if unsupported encoding
        return decompressors // Unsupported encoding
      }

      decompressors.push(supportedEncodings[encoding]())
    }

    return decompressors

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Treat the response as untrusted: do not use the decompress interceptor for that origin, or fix the origin to send a single sane encoding.
  2. If you control the server, ensure Content-Encoding has at most one (or a small, valid) layer.
  3. Catch the error per-request and fall back to consuming the raw (still-encoded) body or failing the request explicitly.

Example fix

// before
client.compose(interceptors.decompress())
// a response with Content-Encoding: gzip, gzip, gzip, gzip, gzip, gzip throws

// after
// exclude the offending origin from decompression, or handle the error:
try {
  await client.request(opts)
} catch (e) {
  if (e.message.startsWith('too many content-encodings')) {
    // consume raw body or reject the response
    throw new Error('Refusing suspicious response from ' + opts.origin)
  }
  throw e
}
Defensive patterns

Strategy: try-catch

Try / catch

try { await client.request(opts) } catch (e) { if (e.message.startsWith('too many content-encodings')) { throw new Error(`Refusing suspicious response from ${opts.origin}`) } else throw e }

Prevention

When it happens

Trigger: A server (often malicious or misconfigured) returns a Content-Encoding header like 'gzip, gzip, gzip, gzip, gzip, gzip' (>5 entries). The decompress interceptor throws synchronously while setting up the decompression pipeline.

Common situations: Security testing/fuzzing; buggy compression middleware that stacks encodings; adversarial origins; intermediary that appends encodings on each hop.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/73b0fdbb2ff44098. Report an issue: GitHub.