Troubleshooting
pdfplumber error index
Paste the last line of your traceback below. Each message here was triggered on purpose with pdfplumber 0.11.10 and pdfminer.six 20260107, so the wording matches what you see, and each comes with the cause and a tested fix.
Find your error
Type or paste part of your error message below to filter the list. Most pdfplumber failures are one of these twenty; each links to a page with the full explanation and tested code. No traceback, just bad output? Jump to the PDF Doctor.
ModuleNotFoundError: No module named 'pdfplumber'ModuleNotFoundError
Cause: pdfplumber is not installed in the interpreter that runs your code.
Fix: Install through that interpreter:
python -m pip install pdfplumber, or%pip install pdfplumberin Jupyter. Details →AttributeError: module 'pdfplumber' has no attribute 'open'AttributeError (Python 3.13+: "consider renaming …"; 3.10–3.12: "partially initialized module …")
Cause: A file or folder named pdfplumber in your project shadows the library.
Fix: Rename your
pdfplumber.pyand delete its__pycache__. Details →NameError: name 'pdfplumber' is not definedNameError
Cause: The file never ran
import pdfplumber.Fix: Add the import, or re-run the import cell after a kernel restart. Details →
PdfminerException: seek of closed filepdfplumber.utils.exceptions.PdfminerException wrapping ValueError
Cause: A page was used after the PDF was closed, typically after the
with pdfplumber.open(...)block ended.Fix: Extract everything inside the with-block (store strings, not pages), or call
pdf.close()yourself when done. Details →PdfminerException: (empty message)PdfminerException wrapping pdfminer.pdfdocument.PDFPasswordIncorrect
Cause: The PDF is encrypted and the password is wrong or missing.
Fix: Open with
pdfplumber.open(path, password="…"). Details →PdfminerException: No /Root object! - Is this really a PDF?PdfminerException wrapping pdfminer.pdfparser.PDFSyntaxError
Cause: The bytes are not a PDF: an HTML error page saved as .pdf, a failed download, or another format.
Fix: Check the file starts with
%PDF-; re-download it. If it is a damaged PDF, tryrepair=True. Details →PdfminerException: Unexpected EOFPdfminerException wrapping pdfminer.psexceptions.PSEOF
Cause: The file is truncated: an interrupted download or upload.
Fix: Get the complete file.
pdfplumber.open(path, repair=True)can sometimes recover the readable pages (needs Ghostscript). Details →TypeError: 'NoneType' object is not iterableTypeError
Cause:
extract_table()found no table and returnedNone, which the loop then tried to iterate.Fix: Check for
None(table = page.extract_table(); if table: …), or useextract_tables(), which returns an empty list. Details →AttributeError: 'NoneType' object has no attribute …AttributeError
Cause: Same pattern: a method returned
None(no table, no search match) and the next call assumed a result.Fix: Guard the result before using it. Details →
ValueError: vertical_strategy must be one of{lines,lines_strict,text,explicit}ValueError
Cause: A misspelled strategy name, such as
"line".Fix: Use one of the four names exactly. Details →
ValueError: If vertical_strategy == 'explicit', explicit_vertical_lines must be specified as a list/tuple of two or more floats/ints.ValueError
Cause: The explicit strategy needs at least two lines to make a column.
Fix: Pass two or more x positions (or objects), or keep another strategy and add explicit lines to it. Details →
ValueError: Table setting 'snap_tolerance' cannot be negativeValueError
Cause: A tolerance or length setting below zero.
Fix: Use zero or a positive number. Details →
ValueError: Bounding box (0, 700, 612, 800) is not fully within parent page bounding box (0, 0, 612, 792)ValueError
Cause:
crop()was given a box that extends past the page (strict mode).Fix: Clamp the box to
page.bbox, or passstrict=False. Details →UnicodeEncodeError: 'charmap' codec can't encode character '\uf0b7' in position 0: character maps to <undefined>UnicodeEncodeError (when printing, mostly on Windows)
Cause: The console uses a legacy code page that cannot show the character; pdfplumber extracted it fine.
Fix: Set
PYTHONUTF8=1, or write to a file withencoding="utf-8". Details →(cid:81)(cid:117)(cid:97)… in extracted textNot an exception: wrong output
Cause: The font has no Unicode mapping.
Fix: OCR the page, or try
chr(N)and verify. Details →extract_text() returns an empty stringNot an exception: empty output
Cause: No text layer: a scanned page, or text drawn as vector outlines.
Fix: Check
len(page.chars)andpage.images; run OCR. Details →Wordsruntogether / S U M M A R Y / TToottaallNot an exception: spacing problems
Cause: Word gaps smaller or larger than
x_tolerance, or fake-bold text drawn twice.Fix:
x_tolerance_ratio, a region-specificx_tolerance, ordedupe_chars(). Details →RecursionError: maximum recursion depth exceededRecursionError
Cause: A malformed PDF with circular or very deep object references.
Fix: Upgrade (0.11.6 handles more of these), then try
repair=True. Details →ImportError: MagickWand shared library not foundImportError (pdfplumber older than 0.10.0)
Cause: Old versions used ImageMagick for
to_image().Fix: Upgrade:
python -m pip install -U pdfplumber. Rendering now uses pypdfium2. Details →Process killed / MemoryError on a large PDFOut of memory
Cause: Pages and their parsed objects stay cached while the PDF is open.
Fix: Call
page.close()after each page, or process in chunks. Details →
No match. Try a shorter phrase from the last line of your traceback, or run the PDF Doctor below.
The messages, reproduced
This script triggers each failure on purpose and prints exactly what Python raised. Most errors from inside PDF parsing reach you as pdfplumber.utils.exceptions.PdfminerException, with pdfminer.six’s original exception inside it (e.args[0]). That is the one to inspect when deciding how to handle a failure:
"""Trigger common pdfplumber errors on purpose and print the exact messages."""
import io
import os
import subprocess
import sys
import pdfplumber
def show(label, fn):
try:
fn()
print(f"{label}: no error")
except BaseException as e: # noqa: BLE001 (we want everything, by design)
msg = str(e).splitlines()[0] if str(e) else ""
print(f"{label}:\n {type(e).__module__}.{type(e).__name__}: {msg[:110]}")
inner = e.args[0] if e.args and isinstance(e.args[0], BaseException) else None
if inner is not None: # pdfplumber wraps pdfminer's exception; show what is inside
print(f" wraps {type(inner).__module__}.{type(inner).__name__}")
def closed_file():
with pdfplumber.open("samples/invoice.pdf") as pdf:
page = pdf.pages[0]
page.extract_text() # used after the with-block closed the file
def wrong_password():
pdfplumber.open("samples/encrypted.pdf", password="wrong").pages[0]
def no_password():
pdfplumber.open("samples/encrypted.pdf").pages[0]
def not_a_pdf():
pdfplumber.open(io.BytesIO(b"<html>this is a web page, not a PDF</html>")).pages
def truncated():
data = open("samples/invoice.pdf", "rb").read()
pdfplumber.open(io.BytesIO(data[: len(data) // 3])).pages[0].extract_text()
def table_none():
with pdfplumber.open("samples/borderless-table.pdf") as pdf:
for row in pdf.pages[0].extract_table(): # None: no table found
print(row)
def bad_strategy():
with pdfplumber.open("samples/invoice.pdf") as pdf:
pdf.pages[0].extract_table({"vertical_strategy": "line"})
def one_explicit_line():
with pdfplumber.open("samples/invoice.pdf") as pdf:
pdf.pages[0].extract_table({"vertical_strategy": "explicit", "explicit_vertical_lines": [100]})
def negative():
with pdfplumber.open("samples/invoice.pdf") as pdf:
pdf.pages[0].extract_table({"snap_tolerance": -1})
for label, fn in [
("page used after the file was closed", closed_file),
("wrong password", wrong_password),
("encrypted, no password given", no_password),
("not a PDF", not_a_pdf),
("truncated download", truncated),
("looping over extract_table() that found nothing", table_none),
("misspelled strategy", bad_strategy),
("explicit strategy with one line", one_explicit_line),
("negative tolerance", negative),
]:
show(label, fn)
# Printing a PDF symbol on a console that uses the legacy Windows cp1252 encoding
code = "print('\\uf0b7 bullet from a PDF')"
env = {**os.environ, "PYTHONIOENCODING": "cp1252"}
result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, env=env)
print("printing a PDF symbol on a cp1252 console:\n ", result.stderr.strip().splitlines()[-1][:110])page used after the file was closed:
pdfplumber.utils.exceptions.PdfminerException: seek of closed file
wraps builtins.ValueError
wrong password:
pdfplumber.utils.exceptions.PdfminerException:
wraps pdfminer.pdfdocument.PDFPasswordIncorrect
encrypted, no password given:
pdfplumber.utils.exceptions.PdfminerException:
wraps pdfminer.pdfdocument.PDFPasswordIncorrect
not a PDF:
pdfplumber.utils.exceptions.PdfminerException: No /Root object! - Is this really a PDF?
wraps pdfminer.pdfparser.PDFSyntaxError
truncated download:
pdfplumber.utils.exceptions.PdfminerException: Unexpected EOF
wraps pdfminer.psexceptions.PSEOF
looping over extract_table() that found nothing:
builtins.TypeError: 'NoneType' object is not iterable
misspelled strategy:
builtins.ValueError: vertical_strategy must be one of{lines,lines_strict,text,explicit}
explicit strategy with one line:
builtins.ValueError: If vertical_strategy == 'explicit', explicit_vertical_lines must be specified as a list/tuple of two or more f
negative tolerance:
builtins.ValueError: Table setting 'snap_tolerance' cannot be negative
printing a PDF symbol on a cp1252 console:
UnicodeEncodeError: 'charmap' codec can't encode character '\uf0b7' in position 0: character maps to <undefineseek of closed file
pdfplumber.open() used as a context manager closes the file when the block ends. Page objects you kept are still linked to it, and the first method that needs to read the file fails. Pull out what you need while the file is open:
import pdfplumber
# Do all the work inside the with-block, or keep the PDF open yourself.
with pdfplumber.open("samples/invoice.pdf") as pdf:
texts = [page.extract_text() for page in pdf.pages]
print(texts[0].splitlines()[0]) # plain strings stay usable after closingHollow Pine Supply Co. INVOICEPassword-protected PDFs
import pdfplumber
with pdfplumber.open("samples/encrypted.pdf", password="plumber") as pdf:
print(pdf.pages[0].extract_text().splitlines()[0])Hollow Pine Supply Co. INVOICEA wrong or missing password raises PdfminerException with an empty message; check isinstance(e.args[0], pdfminer.pdfdocument.PDFPasswordIncorrect) to tell it apart from other failures. PDFs with only an owner password (printing or copying restrictions) open without one. The sample is encrypted.pdf, password plumber.
UnicodeEncodeError: ‘charmap’
This one is not pdfplumber’s: it happens when Python prints a character that the console’s encoding cannot represent, such as the private-use bullet that Word exports. Since Python 3.6 the interactive Windows console prints UTF-8, but output that is redirected to a file or pipe, some IDE consoles and scheduled tasks still use the ANSI code page, cp1252 on Western systems. Fixes, in order: set the environment variable PYTHONUTF8=1; write output to files with encoding="utf-8"; or replace private-use characters (text.replace("", "•")).
Catch errors in batch jobs
When processing thousands of files, catch the wrapped exception per file and keep going:
import logging
import sys
from pathlib import Path
import pdfplumber
from pdfplumber.utils.exceptions import PdfminerException
logging.basicConfig(format="%(levelname)s %(message)s", stream=sys.stdout)
log = logging.getLogger("batch")
texts = {}
for path in sorted(Path("samples/broken").glob("*.pdf")):
try:
with pdfplumber.open(path) as pdf:
texts[path.name] = "\n".join(p.extract_text() for p in pdf.pages)
except PdfminerException as e:
log.warning("%s: %s %s", path.name, type(e.args[0]).__name__, e)
print("read:", sorted(texts))WARNING empty.pdf: PDFSyntaxError No /Root object! - Is this really a PDF?
WARNING html-page.pdf: PDFSyntaxError No /Root object! - Is this really a PDF?
WARNING no-xref.pdf: PDFSyntaxError No /Root object! - Is this really a PDF?
WARNING truncated-33.pdf: PSEOF Unexpected EOF
WARNING truncated-90.pdf: PSEOF Unexpected EOF
read: ['bad-page-stream.pdf', 'bad-startxref.pdf']Run here on the broken sample files: five fail with a logged reason, two open. One of those two, bad-page-stream.pdf, has an empty page that raises nothing, so count characters per page as well.
The performance guide adds timeouts and memory limits for untrusted or very large files.
Diagnose a PDF
For extraction problems without a traceback (empty text, (cid:x), doubled letters, missing tables), the PDF Doctor checks the file page by page. It runs pdfplumber in your browser; your file is not uploaded.
PDF Doctor
Drop a PDF here or choose a sample above.
Text-based PDFs work best. Scanned pages have nothing for pdfplumber to read until OCR adds a text layer.
Text and objects
Run pdfplumber to see extract_text() for this page.
Table Settings Lab
Explicit lines: none
table_settings = # defaults
Run pdfplumber to detect tables.
Crop and coordinates
Drag a rectangle on the page. pdfplumber's coordinates are in points from the top-left corner: (x0, top, x1, bottom).
Drag a box on the page to measure it.
PDF Doctor
Checks why extraction might fail: missing text layer, (cid:x) fonts, duplicated characters, rotation and ruling lines.