ocrmypdf/OCRmyPDF · warning
PDF graphics stack overflowed spec limit
Error message
PDF graphics stack overflowed spec limit
What it means
While interpreting a PDF content stream, more than 32 'q' (save-state) operators were nested without matching 'Q' (restore) — exceeding the PDF spec's graphics state stack limit. The library tolerates up to 128 before a hard RuntimeError; this warning means ink coverage analysis may be imprecise.
Source
Thrown at src/ocrmypdf/pdfinfo/_contentstream.py:192
found_text = False
vector_ops = set(['S', 's', 'f', 'F', 'f*', 'B', 'B*', 'b', 'b*'])
text_showing_ops = set(["TJ", "Tj", '"', "'"])
image_ops = set(['BI', 'ID', 'EI', 'q', 'Q', 'Do', 'cm'])
color_ops = set(['g', 'rg', 'k', 'cs', 'sc', 'scn'])
operator_whitelist = ' '.join(vector_ops | text_showing_ops | image_ops | color_ops)
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)View on GitHub (pinned to 5074a0b0e1)
Solutions
- The warning is non-fatal — processing continues; no action strictly required
- If ink analysis misbehaves, flatten/repair the PDF first (qpdf --stream-data=uncompress, pikepdf)
- Regenerate the offending PDF from its source if you control the generator
Example fix
# before
info = PdfInfo.from_path(path) # may warn on abusive PDFs
# after
import pikepdf
with pikepdf.open(path, allow_overwriting_input=True) as pdf:
pdf.save(path) # normalized copy first
info = PdfInfo.from_path(path) Defensive patterns
Strategy: fallback
Validate before calling
import pikepdf\nwith pikepdf.open(path) as pdf:\n pdf.save(normalized_path) # rebalances/normalizes streams
Try / catch
with warnings.catch_warnings():\n warnings.simplefilter('ignore', UserWarning, message='PDF graphics stack')\n process(path) Prevention
- Normalize machine-generated/abusive PDFs with qpdf or pikepdf first
- Treat stack warnings as signals of generator bugs in your own PDF writer
When it happens
Trigger: Interpreting content streams (used for preprocessing decisions / page ink analysis) of PDFs that stack many q operators — typically machine-generated or malicious/abusive PDFs.
Common situations: PDF generators that emit long q runs; fuzzed/adversarial PDFs; the accompanying test test_stack_abuse exercises exactly this.
Related errors
- PDF graphics stack underflowed - PDF may be malformed
- This PDF contains dynamic XFA forms created by Adobe LiveCyc
- pdfminer could not process page {pageno} (counting from 0).
- pdfminer did not find page {pageno} in the input file.
AI-assisted analysis of ocrmypdf/OCRmyPDF@5074a0b0e1 (2026-08-27).
Data as JSON: /api/errors/785876040c4da944.
Report an issue: GitHub.