mozilla/pdf.js · error · Error

getTextContent - ignoring circular reference: ${objId}

Error message

getTextContent - ignoring circular reference: ${objId}

What it means

Thrown at the start of getTextContent when the stream's objId already appears in prevRefs. It is the text-extraction equivalent of the getOperatorList anti-recursion guard: a Form XObject re-entered during text extraction would otherwise loop. Like [85], this is a bare throw at method entry and is NOT suppressed by ignoreErrors.

Source

Thrown at src/core/evaluator.js:2417

    disableNormalization = false,
    keepWhiteSpace = false,
    prevRefs = null,
    intersector = null,
  }) {
    if (stream.isAsync) {
      const bytes = await stream.asyncGetBytes();
      if (bytes) {
        stream = new Stream(bytes, 0, bytes.length, stream.dict);
      }
    }
    sink ??= textSinkWrapper(null);

    const objId = stream.dict?.objId;
    const seenRefs = new RefSet(prevRefs);

    if (objId) {
      if (prevRefs?.has(objId)) {
        throw new Error(
          `getTextContent - ignoring circular reference: ${objId}`
        );
      }
      seenRefs.put(objId);
    }
    // Ensure that `resources`/`stateManager` is correctly initialized,
    // even if the provided parameter is e.g. `null`.
    resources ||= Dict.empty;
    stateManager ||= new StateManager(new TextState());

    if (includeMarkedContent) {
      markedContentData ||= { level: 0 };
    }

    const textContent = {
      items: [],
      styles: Object.create(null),
      lang,

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Wrap page.getTextContent() in try/catch and fall back (e.g. empty text or skip search) since ignoreErrors will not help here.
  2. Repair the PDF to break the XObject cycle.
  3. Upgrade PDF.js for hardened cycle detection.

Example fix

// before
const tc = await page.getTextContent(); // rejects on cycle

// after
let tc;
try {
  tc = await page.getTextContent();
} catch (e) {
  console.warn('Text extraction failed (circular XObject):', e.message);
  tc = { items: [], styles: {} };
}
Defensive patterns

Strategy: try-catch

Try / catch

// ignoreErrors does NOT suppress this; catch at the getTextContent boundary.
let textContent;
try {
  textContent = await page.getTextContent();
} catch (e) {
  if (/circular reference/.test(e.message)) {
    textContent = { items: [], styles: {} }; // graceful empty result
  } else throw e;
}

Prevention

When it happens

Trigger: getTextContent traverses into a Form XObject whose stream chain re-enters a stream already on the ancestor path (cycle in the XObject reference graph).

Common situations: Malformed or malicious PDFs with cyclic Form XObject references; the symptom specifically affects text extraction / search rather than rendering.

Related errors


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