mozilla/pdf.js · warning · FormatError

FontFile should be a stream

Error message

FontFile should be a stream

What it means

Thrown inside translateFont() when a font descriptor's /FontFile, /FontFile2, or /FontFile3 entry exists but is not a BaseStream (i.e. not a PDF stream object). Embedded font programs must be stream objects carrying the raw font bytes; a non-stream value means the reference is broken. The guard wraps the entire font-file fetch in a try/catch that honors options.ignoreErrors, so with the default render path it degrades to a warning and substitutes a fallback font.

Source

Thrown at src/core/evaluator.js:4620

    }

    if (!(fontName instanceof Name)) {
      throw new FormatError("invalid font name");
    }

    let fontFile, fontFileN, subtype, length1, length2, length3;
    try {
      for (const n of ["FontFile", "FontFile2", "FontFile3"]) {
        fontFile = descriptor.get(n);
        if (fontFile) {
          fontFileN = n;
          break;
        }
      }

      if (fontFile) {
        if (!(fontFile instanceof BaseStream)) {
          throw new FormatError("FontFile should be a stream");
        } else {
          if (fontFile.isAsync) {
            const bytes = await fontFile.asyncGetBytes();
            if (bytes) {
              fontFile = new Stream(bytes, 0, bytes.length, fontFile.dict);
            }
          }
          if (fontFile.isEmpty) {
            throw new FormatError("FontFile is empty");
          }
        }
      }
    } catch (ex) {
      if (!this.options.ignoreErrors) {
        throw ex;
      }
      warn(`translateFont - fetching "${fontName.name}" font file: "${ex}".`);
      fontFile = null;

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Re-embed the font properly using the original source document and a conformant PDF exporter.
  2. Run qpdf --object-streams=disable or Ghostscript to repair the object stream and stream references.
  3. Rely on ignoreErrors (default) — PDF.js will warn and fall back to a standard font; rendering continues with substituted glyphs.
  4. If authoring, validate that each /FontFile* entry is an indirect reference to a stream with /Length, /Subtype (for FontFile3), and byte payload.

Example fix

// before
getDocument({ url, stopAtErrors: true }); // propagates the FormatError

// after
getDocument({ url }); // ignoreErrors=true -> warn + fallback font, render continues
Defensive patterns

Strategy: validation

Validate before calling

// This error is already gated by options.ignoreErrors inside translateFont().
// Use the default render path (no stopAtErrors) to let PDF.js warn + fall back:
const pdf = await getDocument({ url }).promise;
// Bad FontFile references degrade to a warn() and a fallback standard font.

Prevention

When it happens

Trigger: A /FontDescriptor whose /FontFile* entry resolves to a dictionary, number, string, null, or dangling reference instead of a stream. Fires during translateFont() when the descriptor declares an embedded font program that is not actually a stream.

Common situations: Font subsetting/embedding bugs in PDF generators, broken indirect references after linearization corruption, or PDFs where an optimizer replaced the font stream with a stub. Also seen when a FontFile3 references a sub-stream that was dropped during object-stream compression.

Related errors


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