mozilla/pdf.js · error · Error
getOperatorList - ignoring circular reference: ${objId}
Error message
getOperatorList - ignoring circular reference: ${objId} What it means
Thrown at the very start of getOperatorList when the stream's objId already appears in prevRefs (a RefSet tracking ancestor streams). It is an anti-recursion guard: a Form XObject or content stream that (directly or transitively) references itself would otherwise loop forever. Unlike operator-dispatch errors, this is a bare throw at method entry and is NOT suppressed by ignoreErrors.
Source
Thrown at src/core/evaluator.js:1695
resources,
operatorList,
initialState = null,
fallbackFontDict = null,
prevRefs = null,
}) {
if (stream.isAsync) {
const bytes = await stream.asyncGetBytes();
if (bytes) {
stream = new Stream(bytes, 0, bytes.length, stream.dict);
}
}
const objId = stream.dict?.objId;
const seenRefs = new RefSet(prevRefs);
if (objId) {
if (prevRefs?.has(objId)) {
throw new Error(
`getOperatorList - ignoring circular reference: ${objId}`
);
}
seenRefs.put(objId);
}
// Ensure that `resources`/`initialState` is correctly initialized,
// even if the provided parameter is e.g. `null`.
resources ||= Dict.empty;
initialState ||= new EvalState();
if (!operatorList) {
throw new Error('getOperatorList: missing "operatorList" parameter');
}
const self = this;
const xref = this.xref;
const localImageCache = new LocalImageCache();
const localColorSpaceCache = new LocalColorSpaceCache();View on GitHub (pinned to 5903d58d58)
Solutions
- Wrap the page.render() / getOperatorList call in try/catch and present a graceful 'page could not be rendered' result, since ignoreErrors will not help here.
- Repair the PDF to break the cycle in the XObject reference graph.
- Upgrade PDF.js; cycle-detection has been hardened over releases.
Example fix
// before
const page = await pdf.getPage(n);
await page.render({ canvasContext, viewport }).promise; // rejects on cycle
// after
try {
await page.render({ canvasContext, viewport }).promise;
} catch (e) {
console.warn('Unrenderable page (possible circular XObject):', e.message);
} Defensive patterns
Strategy: try-catch
Try / catch
// ignoreErrors does NOT suppress this; catch at the render boundary.
try {
await page.render({ canvasContext, viewport }).promise;
} catch (e) {
if (/circular reference/.test(e.message)) {
showUnrenderablePagePlaceholder(page.pageNumber);
} else throw e;
} Prevention
- Never assume ignoreErrors catches cycle guards; it does not.
- Run incoming PDFs through a repair/sanitize pipeline to break reference loops.
- Give every page render a top-level try/catch in production viewers.
When it happens
Trigger: buildFormXObject calls getOperatorList with prevRefs including the current objId, which happens when a Form XObject's stream chain re-enters a stream already on the ancestor path (cycle in the XObject graph).
Common situations: Malformed or maliciously crafted PDFs with cyclic Form XObject references; PDFs damaged in transit so that object references form a loop.
Related errors
- XObject must be referred to by name.
- XObject should be a stream
- XObject should have a Name subtype
- Unhandled XObject subtype ${type.name}
- getTextContent - ignoring circular reference: ${objId}
AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13).
Data as JSON: /api/errors/607f2cf6c3496283.
Report an issue: GitHub.