mozilla/pdf.js · error · FormatError
bad ObjStm stream
Error message
bad ObjStm stream
What it means
A FormatError raised in XRef.fetchCompressed when the object referenced by a compressed xref entry does not resolve to a BaseStream. Object Streams (ObjStm) must be streams; if the fetch returns a plain dict, null, or a primitive, the compressed object cannot be decoded. It indicates the xref entry's offset points at the wrong object or the object stream itself is malformed.
Source
Thrown at src/core/xref.js:961
? parser.getObj(this.encrypt.createCipherTransform(num, gen))
: parser.getObj();
if (!(xrefEntry instanceof BaseStream)) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
xrefEntry !== undefined,
'fetchUncompressed: The "xrefEntry" cannot be undefined.'
);
}
this.#cacheMap.set(num, xrefEntry);
}
return xrefEntry;
}
fetchCompressed(ref, xrefEntry, suppressEncryption = false) {
const tableOffset = xrefEntry.offset;
const stream = this.fetch(Ref.get(tableOffset, 0));
if (!(stream instanceof BaseStream)) {
throw new FormatError("bad ObjStm stream");
}
const first = stream.dict.get("First");
const n = stream.dict.get("N");
if (!Number.isInteger(first) || !Number.isInteger(n)) {
throw new FormatError("invalid first and n parameters for ObjStm stream");
}
let parser = new Parser({
lexer: new Lexer(stream),
xref: this,
allowStreams: true,
});
const nums = new Array(n);
const offsets = new Array(n);
// read the object numbers to populate cache
for (let i = 0; i < n; ++i) {
const num = parser.getObj();
if (!Number.isInteger(num)) {
throw new FormatError(View on GitHub (pinned to 5903d58d58)
Solutions
- Validate the file with qpdf --check or mutool clean to detect/repair object stream corruption.
- Re-export the PDF from its source application.
- Catch FormatError around page access and skip the affected page rather than failing the whole document.
- If generating PDFs yourself, ensure ObjStm objects are emitted as streams with correct /Type /ObjStm.
Example fix
// before
const page = await doc.getPage(n);
const content = await page.getTextContent(); // throws FormatError
// after
try {
const page = await doc.getPage(n);
const content = await page.getTextContent();
} catch (e) {
if (e instanceof pdfjsLib.FormatError) {
console.warn(`Page ${n} skipped: corrupt object stream`, e.message);
}
} Defensive patterns
Strategy: try-catch
Try / catch
try {
const obj = await doc.getPage(n);
} catch (e) {
if (e instanceof pdfjsLib.FormatError) {
console.warn(`Skipping object/page ${n}: corrupt ObjStm`);
continue;
}
throw e;
} Prevention
- Run qpdf --check on uploaded PDFs before processing.
- Isolate per-page access in try/catch so one bad object stream does not kill the whole document.
- Keep worker and API builds on the same version.
When it happens
Trigger: A compressed xref table entry whose offset field references an object number that is not an ObjStm; a cross-reference where the generator object was overwritten/corrupted; encrypted ObjStm decrypted to a non-stream; manual xref edits that point at the wrong object.
Common situations: PDFs produced by buggy/minimal writers; files repaired by tools that rewrote the xref incorrectly; files damaged in transit that still have a readable trailer but a broken object stream.
Related errors
- invalid first and n parameters for ObjStm stream
- invalid object number in the ObjStm stream: ${num}
- invalid object offset in the ObjStm stream: ${offset}
- Invalid offset in the ObjStm stream.
- Bad (compressed) XRef entry: ${ref}
AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13).
Data as JSON: /api/errors/5be312ef0c7a5c51.
Report an issue: GitHub.