terbium / Guides
Troubleshooting and limits
Fixes for common terbium problems: the old PyPI version, silent OCR, AI calls that never happen, wrong document types, and the known limits of each lane.
On this page
- First principles
- Installation and version
- pip installs 0.9.7 and flags from these docs fail
- pip install terbium installed something else
- pip install -e . fails with the macOS system Python
- A FutureWarning about fitz on every run
- Scanned input and OCR
- An image file gives zero records and no message
- An image of a table or a product grid gives no records, even with Tesseract
- A scanned PDF returns no records
- The catalog message says OCR “already ran”, but Tesseract is not installed
- The AI lane
- I set a key, but nothing changed
- With the AI lane on, no escalation message appears
- read_vision raises instead of returning None
- Classification and lanes
- The document type is wrong
- Resume lines are missing
- A phone number lost its opening bracket
- A receipt has extra line items and two total records
- The invoice summary record has a stray field
- Catalogues
- Two products share one SKU
- --no-images returns no products for a photo catalogue
- catalog_escalation(rows) reports unreadable pages on a healthy catalogue
- Pictures in a PowerPoint deck are missing
- Extra options to build_catalog have no effect on a PowerPoint deck
- Spreadsheets and CSV
- Formula cells come out empty
- Rows past a certain point are missing
- Known limits
Each section names a symptom, its cause in the 0.10.0 source, and what to do. Everything here was checked against the code; the behaviours marked as observed were reproduced with terbium 0.10.0, Python 3.12 and PyMuPDF 2.0 on 25 October 2026.
First principles
Most problems come from one of four places, and it helps to know which before reading the rest:
- The version. The Python Package Index (PyPI) and GitHub serve different releases, and several flags exist only in the newer one.
- The input has no words. Every lane reads words. A scan or an image file has none until Tesseract supplies them, and without Tesseract some paths stay silent.
- The artificial intelligence (AI) lane did not run. It needs a key, the provider’s software development kit (SDK), and a hard table or an incomplete row to work on. Inside
parse, model errors are swallowed so a parse never crashes, which also hides why nothing happened. - A heuristic guessed wrong. Classification, section headings and stock keeping unit (SKU) search are rules, not understanding. Each can be overridden or checked.
Installation and version
pip installs 0.9.7 and flags from these docs fail
PyPI still serves 0.9.7 (checked on 25 October 2026). In 0.9.7, --type, --html and --open do not exist, so the command stops with an unrecognized arguments error. Check with terbium --version, then install 0.10.0 from the GitHub release:
pip install "https://github.com/velofy/terbium/releases/download/v0.10.0/terbium_parse-0.10.0-py3-none-any.whl"
pip install terbium installed something else
The PyPI project named terbium is unrelated. Remove it and install terbium-parse:
pip uninstall terbium
pip install terbium-parse
pip install -e . fails with the macOS system Python
The pip 21.2 that ships with the macOS system Python 3.9 cannot do an editable install from pyproject.toml. Run python -m pip install --upgrade pip first.
A FutureWarning about fitz on every run
With PyMuPDF 2.0, which pip installs by default today, every run prints:
FutureWarning: The `fitz` API is deprecated and will be removed in future.
Use `import pymupdf` instead.
terbium 0.10.0 imports PyMuPDF under its old name, fitz. The warning is harmless today. Silence it with the standard Python warning filter:
PYTHONWARNINGS=ignore::FutureWarning terbium catalogue.pdf
The warning says a future PyMuPDF release will remove the fitz name, and terbium 0.10.0 depends on it.
Scanned input and OCR
An image file gives zero records and no message
Observed. Without Tesseract, the local optical character recognition (OCR) program, an image file (png, jpg, webp, tiff and the rest) becomes a page with no words. parse returns no records and prints nothing, because no table was scored and the image-only check covers Portable Document Format (PDF) pages only.
Check whether terbium can see Tesseract:
python -c "from terbium.layout import ocr; print(ocr.available())"
If it prints False, install Tesseract (brew install tesseract on macOS, apt install tesseract-ocr on Debian and Ubuntu). If Tesseract is installed and it still prints False, set TESSDATA_PREFIX to your tessdata folder. terbium sets it only when the variable is unset or wrong, and only to one of these folders that exists:
/opt/homebrew/share/tessdata/usr/local/share/tessdata/usr/share/tessdata/usr/share/tesseract-ocr/4.00/tessdata/usr/share/tesseract-ocr/5/tessdata/opt/local/share/tessdata
For image files, the adapter runs Tesseract whenever it is available, so ocr=False does not turn OCR off for them. It does for PDF pages.
An image of a table or a product grid gives no records, even with Tesseract
Table reconstruction and label grids run only on PDF pages. For an image file, the recognized words feed classification and the transaction and resume lanes, which read words directly, so a receipt photo works but a photo of a price list does not. OCR on a PDF page feeds the full geometry engine, so wrapping the image in a one-page PDF lets parse rebuild its tables when Tesseract is installed:
from PIL import Image
import terbium
Image.open("pricelist.png").convert("RGB").save("pricelist.pdf")
doc = terbium.parse("pricelist.pdf")
A scanned PDF returns no records
A PDF page with images and no text layer has no words to reconstruct. Without an AI key, parse says so:
terbium: 2 page(s) are image-only with no text layer (e.g. 1, 2). Read them with the vision lane:
pass ai=terbium.AI(...) with a key.
Install Tesseract to read the pages locally, or pass an AI for the vision lane. With a key set, this message is not added.
The catalog message says OCR “already ran”, but Tesseract is not installed
Observed. ocr=True does not install or enable Tesseract; without it, nothing is read. build_catalog then builds its message as if OCR had run, so the message points at the vision lane instead of telling you to install Tesseract. Leave ocr="auto" (the default) to get the accurate message.
The AI lane
I set a key, but nothing changed
Work down this list:
- Is there anything to fix?
parsecalls a model only for tables under the threshold, andbuild_catalogonly for rows missing a name, SKU or materials. The transaction fill is skipped when there is a summary record and every summary record already has atotal. A clean document makes no calls, anddoc.used_aistaysFalse. - Is the SDK installed? Each provider needs its extra, for example
pip install "terbium-parse[anthropic]". Insideparse, a missing SDK raises an error that is caught, so the hard tables stay as they were with no message. - Is the provider name right?
AI(provider=...)acceptsanthropic,openai,kimi,grokorgemini. Any other value, or a pinned provider whose key is missing, means no model is called, and no error is raised. - Is the key really set? An empty environment variable counts as unset.
- Did you pass
True?parse(ai=True)buildsterbium.AI()for you.build_catalog(ai=True)skips the AI fill for photo rows, andenrich_products(records, True)raisesAttributeError. Passterbium.AI()to both.
To see the real error, make one small call through the same provider terbium would pick. This costs one short model call:
import terbium
from terbium.harness.providers import provider_name, text_provider
ai = terbium.AI()
print(ai.available, provider_name(ai)) # False, None: no key was found
print(text_provider(ai).complete("Reply with OK.", "Be brief.", "haiku"))
The last line raises the underlying error, such as RuntimeError: The Anthropic lane needs the 'anthropic' package: pip install terbium-parse[anthropic], an authentication error or an unknown model.
With the AI lane on, no escalation message appears
The message is printed only when no AI is configured. With a key, tables that are still hard after the AI step keep a message in doc.escalation. Read it there.
read_vision raises instead of returning None
read_vision returns None when no provider is available, the page cannot be rendered, or the reply is not JavaScript Object Notation (JSON). Errors from the model call itself, such as a missing SDK, are raised. Wrap the call in try when you run it over many pages.
Classification and lanes
The document type is wrong
Print the scores:
doc = terbium.parse("file.pdf", announce=False)
print(doc.doc_type, doc.class_scores)
The classifier counts keywords and patterns, so prose that mentions “experience” or “invoice” can tip it. Override with doc_type= in Python or --type on the command line.
Resume lines are missing
Observed. A line that contains a section word anywhere (experience, education, skills, projects, certifications and their synonyms, such as employment, expertise or portfolio) is read as a section heading and does not appear as content. For example, “Led a team with deep Python experience” disappears and starts a new experience section. There is no option to change this in 0.10.0.
Also, when the resume’s geometry produces two or more table records, those table records are returned instead of the section records.
A phone number lost its opening bracket
Observed. The phone pattern starts matching at the first digit, so (555) 123-4567 comes out as 555) 123-4567.
A receipt has extra line items and two total records
Observed. When a receipt has no line item table, terbium builds line items from the page layout, and any line that ends in a number counts. Receipt No: R-8891 became a line item with description Receipt No: R and amount 8891, and the Total: line produced two total records. These records score 0.65 and 0.7, so at the default threshold they are always in doc.ambiguous_records. Keep line items whose amount carries a currency symbol, or review them. Details: What to check in transaction output.
The invoice summary record has a stray field
Observed. The summary record collects every Label: value pair and stacked label it finds on the page. A table header line followed by a row can be read as a label above its value, so a summary can carry, for example, a description holding the first line item. Line items also copy the header fields, including the invoice total. Select the fields you need by record_type.
Catalogues
Two products share one SKU
Observed. The SKU search covers the text below, beside and above each photo, then the whole page. A labelled SKU: line anywhere in that zone wins. So a product with no printed code can receive a neighbour’s SKU. Check for repeats before you import:
from collections import Counter
counts = Counter(r["sku"] for r in rows if r["sku"])
repeated = [r for r in rows if r["sku"] and counts[r["sku"]] > 1]
--no-images returns no products for a photo catalogue
Observed. Without an image folder, build_catalog cannot anchor rows on photos and falls back to table rows. A lookbook has no product table, so the result can be empty. Use --images DIR instead.
catalog_escalation(rows) reports unreadable pages on a healthy catalogue
Observed. build_catalog removes the nearby text it kept for each row before returning, and terbium.catalog.catalog_escalation treats a row with an image and no nearby text as unreadable. Called on the returned rows, it reports every photo page. Rely on the message build_catalog prints itself, with announce=True.
Pictures in a PowerPoint deck are missing
Observed. Only pictures placed directly on a slide become rows. Pictures inside a group shape, and pictures inserted into a picture placeholder, are skipped. Ungroup them, or paste them as plain pictures, and save the deck again.
Extra options to build_catalog have no effect on a PowerPoint deck
Keyword arguments such as only_photos or min_side go to export_images, which handles PDF files. The PowerPoint (PPTX) path ignores them.
Spreadsheets and CSV
Formula cells come out empty
Observed. terbium reads the values that Excel stored the last time it calculated the sheet, not the formulas. A file written by a script, such as one made with openpyxl, may have no stored values, so its formula cells are empty and drop out of the records. Open and save the file in Excel or LibreOffice first.
Rows past a certain point are missing
The adapters stop reading at 5,000 rows and 200 columns per Excel (XLSX) sheet, and at 20,000 rows per comma-separated values (CSV) file. Nothing is printed when this happens. Split larger files.
Known limits
- Table reconstruction from geometry runs only on PDF pages. Tables drawn with loose text boxes on a slide, or text in an image file, are not rebuilt into tables.
- A table without a header row loses its first row: that row becomes the column headers.
- Confidence measures a table’s shape, not the truth of its values. See How it works.
- The HyperText Markup Language (HTML) output loads the Inter font from Google Fonts when it is opened, and falls back to the system font offline.
- From the 0.10.0 changelog: image input to
o3-mini(theopustier on OpenAI) is unverified, model identifiers (IDs) are not verified against the provider application programming interfaces (APIs), and the Gemini lane usesgoogle-generativeai, which Google has superseded withgoogle-genai. - The repository has no continuous integration (CI). Tests run only when someone runs them locally.