ocrmypdf/OCRmyPDF · warning

PDF graphics stack underflowed - PDF may be malformed

Error message

PDF graphics stack underflowed - PDF may be malformed

What it means

A 'Q' (restore graphics state) operator was encountered with an empty state stack — the PDF is unbalanced/malformed. The interpreter keeps the current state and warns; downstream ink analysis may be slightly off but processing continues.

Source

Thrown at src/ocrmypdf/pdfinfo/_contentstream.py:199

    for n, graphobj in enumerate(
        _normalize_stack(parse_content_stream(contentstream, operator_whitelist))
    ):
        operands, operator = graphobj
        if operator == 'q':
            stack.append((ctm, fill_ink, fill_space))
            if len(stack) > 32:  # See docstring
                if len(stack) > 128:
                    raise RuntimeError(
                        f"PDF graphics stack overflowed hard limit at operator {n}"
                    )
                warn("PDF graphics stack overflowed spec limit")
        elif operator == 'Q':
            try:
                ctm, fill_ink, fill_space = stack.pop()
            except IndexError:
                # Keeping the state the same seems to be the only sensible thing
                # to do. Just pretend nothing happened, keep calm and carry on.
                warn("PDF graphics stack underflowed - PDF may be malformed")
        elif operator == 'cm':
            try:
                ctm = Matrix(operands) @ ctm
            except ValueError as e:
                raise InputFileError(
                    "PDF content stream is corrupt - this PDF is malformed. "
                    "Use a PDF editor that is capable of visually inspecting the PDF."
                ) from e
        elif operator == 'g':
            if vals := _operand_floats(operands):
                fill_ink = _ink_from_components('gray', vals)
                fill_space = '/DeviceGray'
        elif operator == 'rg':
            if vals := _operand_floats(operands):
                fill_ink = _ink_from_components('rgb', vals)
                fill_space = '/DeviceRGB'
        elif operator == 'k':
            if vals := _operand_floats(operands):

View on GitHub (pinned to 5074a0b0e1)

Solutions

  1. No action needed unless ink-based preprocessing decisions look wrong
  2. Repair/normalize the PDF with qpdf or pikepdf to rebalance the content stream
  3. If you generate PDFs yourself, ensure every q has a matching Q

Example fix

# before
# process original.pdf directly (warning printed)
// after
qpdf --object-streams=disable original.pdf fixed.pdf  # then process fixed.pdf
Defensive patterns

Strategy: fallback

Try / catch

with warnings.catch_warnings():\n    warnings.simplefilter('ignore', UserWarning, message='PDF graphics stack underflowed')\n    process(path)

Prevention

When it happens

Trigger: Interpreting a content stream where Q operators outnumber q operators — hand-edited or broken PDF content streams.

Common situations: PDFs from buggy generators, hand-crafted content streams, or files that survived partial corruption; usually harmless to output quality.

Understand the failure class

Related errors


AI-assisted analysis of ocrmypdf/OCRmyPDF@5074a0b0e1 (2026-08-27). Data as JSON: /api/errors/2ccfda5dfee9c0a9. Report an issue: GitHub.