mozilla/pdf.js · error · JpegError

unexpected marker ${((bitsData << 8) | nextByte).toString(16

Error message

unexpected marker ${((bitsData << 8) | nextByte).toString(16)}

What it means

Thrown as JpegError when readBit encounters a non-zero byte after 0xFF that is neither DNL (0xDC) nor EOI (0xD9) — i.e. an unexpected JPEG marker embedded inside the scan data. The scan bit-stream should only ever byte-stuff 0xFF00; any other marker is invalid mid-scan.

Source

Thrown at src/core/jpg.js:186

            // parsed number of scanLines when it's at least (approximately)
            // one "half" order of magnitude smaller than expected (fixes
            // issue10880.pdf, issue10989.pdf, issue15492.pdf).
            if (
              maybeScanLines > 0 &&
              Math.round(frame.scanLines / maybeScanLines) >= 5
            ) {
              throw new DNLMarkerError(
                "Found EOI marker (0xFFD9) while parsing scan data, " +
                  "possibly caused by incorrect `scanLines` parameter",
                maybeScanLines
              );
            }
          }
          throw new EOIMarkerError(
            "Found EOI marker (0xFFD9) while parsing scan data"
          );
        }
        throw new JpegError(
          `unexpected marker ${((bitsData << 8) | nextByte).toString(16)}`
        );
      }
      // unstuff 0
    }
    bitsCount = 7;
    return bitsData >>> 7;
  }

  function decodeHuffman(tree) {
    let node = tree;
    while (true) {
      node = node[readBit()];
      switch (typeof node) {
        case "number":
          return node;
        case "object":
          continue;

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Re-encode the source JPEG with a standard encoder to eliminate stray markers.
  2. Validate the JPEG with jpegtran/djpeg before embedding to catch structural errors.
  3. Catch JpegError at render time and substitute a placeholder image.
  4. Regenerate the PDF with a known-good image and re-render.
Defensive patterns

Strategy: try-catch

Validate before calling

// No general pre-check; you can scan for stray markers mid-scan, but it is
// expensive. As a sanity gate, confirm the stream starts with SOI (0xFFD8):
function jpegHasSoi(bytes) {
  return bytes instanceof Uint8Array && bytes.length >= 2 &&
    bytes[0] === 0xff && bytes[1] === 0xd8;
}

Type guard

function isJpegError(err) {
  return err?.name === 'JpegError' && /unexpected marker/.test(err?.message || '');
}

Try / catch

try { await page.render({ canvasContext }).promise; }
catch (err) {
  if (err?.name === 'JpegError' && /unexpected marker/.test(err.message)) {
    console.warn('Corrupt JPEG marker; substituting placeholder image.');
  } else throw err;
}

Prevention

When it happens

Trigger: Decoding a JPEG whose scan data contains a stray marker (e.g. 0xFFC0-style restart/SOF marker) at a position where a stuffed byte or entropy data was expected. Reached during DCTDecode image rendering of a corrupt or non-conformant JPEG.

Common situations: Truncated/concatenated JPEGs, restart markers in the wrong place, JPEGs damaged by a copy/merge, or streams produced by encoders that emit non-standard markers mid-scan.

Related errors


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