denoland/deno · error · TypeError

brotli decompression failed

Error message

brotli decompression failed

What it means

Deno's DecompressionStream for 'brotli': when the stream finishes (writer closed), the underlying brotli decoder is finalized via into_inner(); if the compressed data ended mid-stream (incomplete brotli stream) or is corrupt, finalization fails and the stream errors with ErrorKind::InvalidData. Note the wrapper suppresses errors when report_errors is false, in which case the symptom is a silently empty/truncated result instead of this error.

Source

Thrown at ext/web/compression.rs:289

    .take()
    .ok_or(CompressionError::ResourceClosed)?;
  let out = match inner {
    Inner::DeflateDecoder(d) => {
      d.finish().map_err(CompressionError::IoTypeError)
    }
    Inner::DeflateEncoder(d) => {
      d.finish().map_err(CompressionError::IoTypeError)
    }
    Inner::DeflateRawDecoder(d) => {
      d.finish().map_err(CompressionError::IoTypeError)
    }
    Inner::DeflateRawEncoder(d) => {
      d.finish().map_err(CompressionError::IoTypeError)
    }
    Inner::GzDecoder(d) => d.finish().map_err(CompressionError::IoTypeError),
    Inner::GzEncoder(d) => d.finish().map_err(CompressionError::IoTypeError),
    Inner::BrotliDecoder(d) => d.into_inner().map_err(|_| {
      CompressionError::IoTypeError(std::io::Error::new(
        std::io::ErrorKind::InvalidData,
        "brotli decompression failed",
      ))
    }),
    Inner::BrotliEncoder(d) => d.finish(),
  };
  match out {
    Err(err) => {
      if report_errors {
        Err(err)
      } else {
        Ok(Vec::with_capacity(0).into())
      }
    }
    Ok(out) => Ok(out.into()),
  }
}

View on GitHub (pinned to a961cdec3b)

Solutions

  1. Verify the source integrity before decompressing (Content-Length match, checksum/hash of the .br payload).
  2. Re-download or regenerate the brotli artifact from a trusted source.
  3. Catch the stream error explicitly and report which asset failed instead of propagating mid-pipe.
  4. If responses come from your own server, ensure it does not close the body early (check compression middleware ordering).

Example fix

// before
const text = await new Response(fileStream.pipeThrough(new DecompressionStream("brotli"))).text(); // truncated .br -> InvalidData (or silent empty)

// after
const bytes = new Uint8Array(await new Response(fileStream).arrayBuffer());
if (bytes.length < 8) throw new Error("brotli payload implausibly small — likely truncated");
try {
  const text = await new Response(new Blob([bytes]).stream().pipeThrough(new DecompressionStream("brotli"))).text();
} catch (e) {
  throw new Error(`corrupt brotli asset (${bytes.length} bytes): ${e.message}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const res = await fetch(url);
const enc = res.headers.get("content-encoding");
if (enc && enc !== "br" && enc !== "gzip" && enc !== "deflate") throw new Error(`unsupported content-encoding: ${enc}`);
if (res.headers.has("content-length") && Number(res.headers.get("content-length")) < 4) throw new Error("body too small to be a valid compressed stream");

Try / catch

try { text = await decompress(bytes); } catch (e) { if (/brotli decompression failed/.test(String(e))) throw new Error(`corrupt/truncated brotli payload for ${url} — re-fetch the source`); throw e; }

Prevention

When it happens

Trigger: Piping truncated or corrupt brotli data through DecompressionStream('brotli') and then closing the writer: cut-off downloads, files truncated by disk limits, or upstream errors where Content-Length promised more bytes than the compressed stream contained.

Common situations: Decompressing .br assets whose download was interrupted; proxy-truncated responses; concatenating or hand-editing brotli files; precompressed artifacts produced by a different/older brotli version.

Related errors


AI-assisted analysis of denoland/deno@a961cdec3b (2026-08-20). Data as JSON: /api/errors/5110631b803c743d. Report an issue: GitHub.