mozilla/pdf.js · error · FormatError

Bad FCHECK in flate stream: ${cmf}, ${flg}

Error message

Bad FCHECK in flate stream: ${cmf}, ${flg}

What it means

Thrown by the FlateStream constructor when the FCHECK invariant (((CMF<<8)+FLG) % 31 !== 0) fails. RFC 1950 requires that the 16-bit value formed by CMF and FLG be a multiple of 31 so decoders can detect header corruption. A failure means the two header bytes are inconsistent — either truncated, bit-flipped, or not actually a zlib header. This is the third of four sequential header guards.

Source

Thrown at src/core/flate_stream.js:144

  constructor(str, maybeLength) {
    super(maybeLength);

    this.stream = str;
    this.dict = str.dict;

    const cmf = str.getByte();
    const flg = str.getByte();
    if (cmf === -1 || flg === -1) {
      throw new FormatError(`Invalid header in flate stream: ${cmf}, ${flg}`);
    }
    if ((cmf & 0x0f) !== 0x08) {
      throw new FormatError(
        `Unknown compression method in flate stream: ${cmf}, ${flg}`
      );
    }
    if (((cmf << 8) + flg) % 31 !== 0) {
      throw new FormatError(`Bad FCHECK in flate stream: ${cmf}, ${flg}`);
    }
    if (flg & 0x20) {
      throw new FormatError(`FDICT bit set in flate stream: ${cmf}, ${flg}`);
    }

    this.codeSize = 0;
    this.codeBuf = 0;
  }

  async getImageData(length, _decoderOptions) {
    const data = await this.asyncGetBytes();
    if (!data) {
      return this.getBytes(length);
    }
    if (data.length <= length) {
      return data;
    }
    return data.subarray(0, length);

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Re-acquire the PDF from a trusted source to rule out bit-rot or transfer corruption.
  2. Repair with qpdf (which can often decode past minor corruption) or re-export via Ghostscript.
  3. Catch at the page level and continue rendering other pages; this error is stream-local.
  4. If authoring, run your zlib output through a decoder round-trip test to verify the header checksum before publishing.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await page.render({ canvasContext, viewport });
} catch (e) {
  if (e.name === 'FormatError' && /Bad FCHECK in flate stream/.test(e.message)) {
    // zlib header checksum failed — bit-level corruption; re-acquire the PDF
  } else throw e;
}

Prevention

When it happens

Trigger: A /FlateDecode stream whose first two bytes are present and the method nibble is 8, but the modulo-31 checksum is wrong — indicating byte-level corruption of the header or a header that merely happens to have CM=8. Fires during FlateStream construction before any actual deflate decoding begins.

Common situations: Bit-level corruption from storage/network errors, PDFs processed by tools that patched bytes incorrectly, or generators with off-by-one header-encoding bugs. Less common than truncation but typical of partially-recovered or repaired PDFs.

Related errors


AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13). Data as JSON: /api/errors/fc09fc2948c3d130. Report an issue: GitHub.