These docs describe changes that aren’t released yet. For the newest release, see the released docs.
Error and warning codes
Every error and warning Lamina PDF raises, in Rust, in WebAssembly and in @laminapdf/sdk, has one of these codes. The tables below are read from fixtures/codes.json when this site is built: the same file cargo xtask gen codes generates the Rust enums and the TypeScript unions from. Each code links to its page on this site, which explains when it happens, why, how to fix it and what its data holds. See Errors and warnings for how to handle them.
Warnings
| Code | Kind | What happened | What to do |
|---|---|---|---|
missing-header | input | no %PDF-x.y header in the first 1,024 bytes; parsing went ahead | nothing to do if the document reads correctly; open in strict mode to refuse such files |
xref-repaired | input | the stored cross-reference data was unusable and was rebuilt by scanning | check the result; save with a full rewrite, since an incremental update refuses a repaired base |
xrefstm-ignored | input | a hybrid file's /XRefStm could not be read and was ignored | nothing to do; the classic xref table is used instead |
stream-length-recovered | input | a stream's /Length was wrong, so its end was found by scanning for endstream | nothing to do; a full rewrite writes the correct length |
stale-generation | input | a reference's generation did not match its xref entry, so it resolved to null | nothing to do; the reference is treated as missing |
page-tree-node-skipped | input | a page-tree node was missing, malformed or too deep, and was skipped | check the page count: pages under the skipped node are missing |
page-count-mismatch | input | the page tree's /Count differs from the number of pages walked | nothing to do; Lamina PDF uses the walked page count |
flate-truncated | input | Flate data was truncated or had a bad checksum; the partial output was kept | check the affected content: the end of the stream may be missing |
objstm-index-mismatch | input | an object stream's index was stale and the object was found by searching | nothing to do; a full rewrite writes a correct index |
object-number-out-of-range | limit | an xref entry above max_object_number was dropped | raise max_object_number in Limits only if you trust the input |
aes-padding-invalid | security | AES-encrypted data had a bad length or padding and was kept as stored | treat the affected strings and streams as suspect: they may have been tampered with |
perms-mismatch | security | an R5/R6 document's /Perms does not match its /P and /EncryptMetadata | treat the document's permissions as untrusted |
warnings-truncated | limit | more than max_warnings warnings were raised and the rest were dropped | raise max_warnings in Limits if you need the full list |
invalid-box | input | a page boundary box is not four numbers and reads as absent | nothing to do; the page uses the next box in the fallback chain |
invalid-rotate | input | a page's /Rotate is not a multiple of 90 and reads as 0 | set the page rotation explicitly if 0 is wrong |
name-tree-node-skipped | input | a name- or number-tree node was malformed, repeated or too deep, and was skipped | check the tree's entries: those under the skipped node are missing |
outline-cycle | input | an outline item was reached twice; that branch of the walk stopped | nothing to do; the outline is read without the loop |
outline-item-skipped | input | an outline item was missing, not a dictionary or too deep, and was skipped with its subtree | check the outline: the skipped item and its children are missing |
invalid-date | input | an Info date is not a valid PDF date; the raw string is kept | read the raw value from the Info view if you need it |
invalid-page-label | input | a /PageLabels entry is malformed; the malformed part is dropped | nothing to do; affected pages fall back to decimal labels |
invalid-entry | input | an entry a view reads is unreadable or of the wrong type and reads as absent | nothing to do; the view uses the entry's default |
content-unknown-operator | input | an operator the spec does not define was skipped outside a BX/EX section | nothing to do; check the page's rendering if the operator mattered |
content-bad-operands | input | an operator had too few operands or operands of the wrong type, or extra ones | nothing to do; the operator was skipped or used its last operands |
content-unbalanced-q | input | a Q without a matching q was ignored, or saved graphics states were left open | nothing to do; the graphics state was restored as the spec intends |
content-unbalanced-bt | input | a text object was opened twice, closed outside one, or left open | nothing to do; text operators outside a text object were handled tolerantly |
content-unbalanced-marked | input | an EMC without a matching BMC or BDC was ignored, or marked content was left open | check tagged-content results: marked-content sequences were closed for you |
content-syntax | input | content-stream bytes that are not a token were skipped | check the page's content: the bytes around the reported offset were skipped |
content-resource-missing | input | an operator named a resource that is missing, of the wrong type or unreadable | check the page's resources: the operator ran without the resource or was skipped |
inline-image-unterminated | input | no EI ends an inline image; the rest of the content was skipped | check the page's content: everything after the inline image is missing |
xobject-cycle | input | a Form XObject draws itself, directly or through other forms | nothing to do; the inner Do was skipped |
content-path-unpainted | input | a content stream ended with a path no painting operator ended | nothing to do; the unpainted path was dropped as the spec intends |
font-invalid | input | a font dictionary has a missing or malformed entry, so its text was decoded with defaults | nothing to do; extracted text or positions for that font may be off |
cmap-invalid | input | a font's CMap or ToUnicode CMap is malformed or names a predefined CMap Lamina PDF has no table for | nothing to do; the bad entries were skipped and the rest of the CMap is used |
unmapped-glyph | input | a font shows glyphs that nothing maps to text | nothing to do; those glyphs extract as U+FFFD (the file needs a ToUnicode CMap) |
extract-limit-blocks | limit | a page had more runs of text than the extraction block limit; the runs past it were left out | raise ExtractLimits::max_blocks_per_page if the page really has that many blocks |
extract-reading-order-ambiguous | unsupported | a page mixes horizontal and vertical writing, so the order of its blocks is a guess | nothing to do; check the order of that page's blocks, or ask for content order |
extract-table-ambiguous | unsupported | a table's rules marked out merged cells that aren't rectangles, so its cells were kept unmerged | nothing to do; check that table's cells, whose text may be split across neighbours |
extract-table-limit | limit | a page's ruling lines would make a table bigger than the extraction's cell limit; those lines gave no table | raise ExtractLimits::max_table_cells if the page really has a table that big |
extract-struct-tree-ignored | input | a tagged page's structure tree covered too little of its text, or its order jumped back across columns too often, so the layout gave the reading order | nothing to do; ReadingOrder::Tagged uses the tree anyway |
extract-untagged-content | input | a page read in structure-tree order has visible text that is neither in the tree nor an artifact; it follows the tree's blocks, in layout order | nothing to do; tag that text in the file to give it a place in the reading order |
Errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
not-pdf | input | the input does not look like a PDF at all | check that the bytes passed to open are a PDF file |
missing-root | input | no document catalog (/Root) was found, even after repair | use another copy of the file: this one has no usable catalog |
reference-cycle | input | a reference chain or the page tree loops back on itself | use another copy of the file, or repair it with another tool |
limit-exceeded | limit | a hard Limits value was exceeded | the input exceeds a safety limit; raise {limit} in Limits only if you trust the input |
strict | input | strict mode refused something the tolerant reader would have repaired | open without strict mode to read the file with repairs, or fix the file |
data-pending | state | the ByteSource has not fetched the byte ranges the call needs yet | fetch the listed ranges into the source and retry the call |
source | io | the ByteSource failed to read | retry the call, or check the source; the document stays usable |
type | input | an object had a different type than the structure requires | use another copy of the file, or repair it with another tool |
invalid-structure | input | an object stream, xref section or filter parameter was malformed | use another copy of the file, or repair it with another tool |
unset-object | internal | a writer object number was reserved but never given a value | this is a bug in Lamina PDF: report it with the steps that led to it |
repaired-base | state | an incremental update was requested for a document whose xref needed repair | save with a full rewrite instead of an incremental update |
encrypted-base | unsupported | a save of an encrypted document was requested | saving encrypted documents is not supported yet; use another tool to decrypt or re-encrypt |
password-required | security | the document is encrypted with a user password and none was given | pass the document's password in OpenOptions |
wrong-password | security | the given password is neither the user nor the owner password | ask for the password again and retry with the correct one |
unsupported-security-handler | unsupported | the document uses a security handler Lamina PDF cannot decrypt | use a tool that supports this encryption to decrypt the file first |
unexpected-eof | input | the input ended before a complete token or object was read | use a complete copy of the file: this one looks truncated |
syntax | input | a token or structure did not match the PDF grammar | use another copy of the file, or repair it with another tool |
unsupported-filter | unsupported | a stream uses a filter Lamina PDF does not implement | use a tool that supports the filter to read this stream |
decompress | input | compressed stream data was corrupt | use another copy of the file: this stream's data is damaged |
Op errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
nested-transaction | state | a transaction was started, or undo or redo called, while one is open | commit or roll back the open transaction first, or use a savepoint inside it |
no-transaction | state | apply, commit or rollback was called without an open transaction | call begin first, or use transact |
unknown-page | usage | an op names a page the document does not have | use a PageId from this document's pages() |
direct-page | unsupported | the page is a direct dictionary, which ops cannot edit yet | rewrite the file so that every page is an indirect object, or wait for page-structure ops |
unsupported-version | unsupported | an op log has a schema version this reader does not know | upgrade Lamina PDF to a version that reads this op log version |
invalid-op-log | input | an op log does not parse: an unknown op or field, a bad value, or not JSON | validate the op log against the op schema; the samples in fixtures/ops show the format |
saved-not-rebased | state | the document was saved to a sink Lamina PDF cannot read back and waits for rebase | call rebase with the saved bytes before applying ops or saving again |
no-pending-save | state | rebase was called without a save waiting for it | call rebase only after a save that returned SavedNotRebased |
Save errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
sink | io | the sink failed to take the bytes or could not give them back | check the destination and retry the save; the document is unchanged |
not-rebased | state | the output was written, but rebasing the document onto it failed | retry rebase with the saved output carried by the error |
sink-not-empty | usage | the sink already held bytes before the save started | pass an empty sink: output offsets count from the sink's first byte |
would-invalidate-signatures | security | the save would break one or more signatures | save with SaveMode::Auto, or ask the user and retry with SaveOptions::with_invalidate_signatures |
requires-rewrite | state | an incremental save was requested but only a full rewrite is safe | save with SaveMode::Auto or SaveMode::FullRewrite |
Text errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
invalid-font | input | font bytes could not be read as a font | pass the complete bytes of a .ttf, .otf, .ttc or .otc file |
collection-index | usage | a font collection member past the end of the collection was asked for | use an index below Font::collection_len |
unknown-axis | usage | an instance was asked for on a variation axis the font does not have | check the font's axes before choosing coordinates, or use a variable font |
font-not-embeddable | usage | the font's OS/2 fsType forbids embedding | use another font, or register it with EmbedPolicy::Override if your licence allows embedding |
missing-glyph | input | the font has no glyph for a character of the text | use a font that covers the character, or split the text across fonts |
glyph-out-of-range | usage | a glyph ID at or past the font's glyph count was used | pass glyph IDs from the font you registered |
unknown-font | usage | a FontKey was used with a registry that did not hand it out | use the key returned by this registry's add |
subset-failed | unsupported | subsetting a font failed | try another version or format of the font, and report the font if it is a common one |
invalid-axis-value | usage | a variation axis value was NaN or infinite | pass finite axis values; values outside the axis range are clamped |
wrong-document | usage | finish_fonts was given a document the registry did not reserve its fonts in | use one FontRegistry per document and finish it with that document |
invalid-feature | usage | an OpenType feature setting did not parse | write it as liga=0, -liga, smcp or aalt=2 (HarfBuzz syntax without ranges) |
invalid-text-range | usage | a byte range was out of bounds or not on character boundaries of the text | take ranges from Lamina PDF's results (line_breaks, itemize, break_lines) and pass the same text |
unsupported-layout-option | unsupported | a layout option that is not implemented yet was asked for | leave the option at its default; justification comes with a later release |
font-mismatch | usage | a shaped run was given with a font key for a different font than it was shaped with | pass the key of the font the run was shaped with (the font index of each line run maps to your keys) |
encoding-full | limit | a standard 14 font has no free one-byte code left for another glyph | embed a font with Font::from_bytes, or split the text across two registrations of the standard font |
font-not-shapable | unsupported | a standard 14 font was given to shaping or layout, which need a font program | draw standard 14 fonts with FontRegistry::encode, or shape with an embedded font |
Text warnings
| Code | Kind | What happened | What to do |
|---|---|---|---|
tounicode-conflict | usage | one glyph was used for two different texts; ToUnicode keeps the first | mark the second occurrence with /ActualText so extraction gets its text |
whole-font-rebuilt | usage | a font that forbids subsetting was rebuilt with every glyph kept | check that the font's licence allows this, or use a static TrueType or CFF version |
Build errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
invalid-image | input | image bytes are not a readable image of the format they were given as | pass the complete bytes of a JPEG or PNG file to Image::jpeg or Image::png |
unsupported-image | unsupported | the image uses a feature that PDF or Lamina PDF does not support | convert it to a baseline or progressive 8-bit JPEG, or to PNG |
image-too-large | limit | the image needs more memory than the builder allows (a decoded PNG over 256 MiB, or raw dimensions whose size overflows) | scale the image down before adding it |
image-data-length | usage | raw image samples are not width x height x channels bytes long | pass exactly 4 bytes (RGBA) per pixel, row by row |
invalid-icc-profile | input | the bytes are not a usable ICC profile | pass a complete ICC profile with a Gray, RGB or CMYK data colour space |
color-components | usage | a colour has the wrong number of components for its colour space | give one component per channel of the colour space (/N of the ICC profile) |
invalid-color | usage | a colour string is not of the form #rgb or #rrggbb | write the colour as # followed by 3 or 6 hexadecimal digits |
invalid-geometry | usage | a page size, box, /UserUnit, drawing value (coordinate, matrix, line width, dash, miter limit) or image size is not usable | use finite coordinates, positive page sizes and /UserUnit, non-negative line widths and dashes, a miter limit of at least 1, and images of at least one pixel |
text-overflow | usage | a text box's lines don't all fit its height and its options asked for an error | make the box taller or the text shorter, or use Overflow::Clip and read TextLayout::overflow |
page-index-out-of-range | usage | a link, outline item or destination names a page index the document doesn't have | use a 0-based index below the document's page count |
unknown-destination | usage | a link or outline item names a destination that was never defined | define it with Builder::destination before the build finishes, or link to a page |
invalid-page-labels | usage | page label ranges that don't start at page 0, don't increase, start past the last page or number from 0 | give ranges sorted by first page, the first at page 0, numbered from 1 |
profile-version-conflict | usage | the PDF version asked for is one the profile doesn't allow (PDF/UA-1, PDF/A-2 and PDF/A-3 are based on PDF 1.7) | leave the version at 1.7 (the default) or lower; PDF 2.0 needs PDF/A-4 or PDF/UA-2, which Lamina PDF doesn't write yet |
missing-title | usage | PDF/UA-1 needs a document title, and the metadata gives none | set a title with Builder::metadata (Metadata::with_title) |
missing-lang | usage | PDF/UA-1 needs the document's natural language, and none was set | set the language with Builder::lang, such as "en-GB" |
missing-alt-text | usage | a Figure or Formula has no alternate text (/Alt or /ActualText), which PDF/UA-1 requires | give the figure or formula a description with Tag::with_alt or Surface::figure's alt, or draw decoration as an artifact |
untagged-content | usage | content was drawn outside every structure element and artifact, which PDF/UA-1 forbids | draw it inside Surface::tag (real content) or Surface::artifact (decoration) |
heading-level-skipped | usage | a heading skips a level (the first is not H1, or an H3 follows an H1), which PDF/UA-1 forbids | start with H1 and go down one level at a time |
unnumbered-heading-misused | usage | an unnumbered H heading shares its parent with another H, or the document also uses H1-H6, which PDF/UA-1 forbids | use numbered headings (H1, H2, ...) throughout, or one H per section |
invalid-structure-nesting | usage | a table or list element sits in a parent its type doesn't allow (a TD outside a TR, a P directly in a Table) | nest tables as Table > TR > TH/TD and lists as L > LI > Lbl/LBody |
link-without-structure | usage | a link annotation has no Link structure element (it was drawn inside an artifact), which PDF/UA-1 forbids | draw links outside artifacts, so the builder gives each one a Link element |
link-without-contents | usage | a link annotation has no description (/Contents), which PDF/UA-1 requires | pass a description as the link's contents |
font-not-embedded | usage | a standard 14 font, which is never embedded, was used under a profile that needs every font embedded | load a font file with Builder::font instead of Builder::standard_font |
glyph-without-unicode | usage | a glyph was drawn without a Unicode value, which PDF/A-2u and PDF/UA-1 forbid | use text whose glyphs each stand for characters, or choose PDF/A-2b, which doesn't need Unicode |
device-color-without-output-intent | usage | a DeviceCMYK colour or image was used, but the PDF/A output intent is sRGB | use RGB or gray colours, or a CMYK colour with an ICC profile (Color::icc) |
embedded-file-forbidden | usage | PDF/A-2 doesn't allow attached files that aren't PDF/A themselves | choose PDF/A-3b, which allows attached files of any kind |
embedded-file-without-mime-type | usage | an attached file has no MIME type, which every PDF/A profile requires | pass the file's MIME type, such as text/xml or application/octet-stream |
Render errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
render-too-large | limit | the requested tile is larger than the render limits allow; nothing was allocated | request smaller tiles (256 to 512 pixels at high zoom) or raise RenderLimits if the larger bitmap is intended |
render-failed | input | the render engine could not open the document or draw the page | the file may be damaged or use a feature this engine can't draw; try the other backend, or report the file |
render-backend-unavailable | io | the render engine is not available: its library or module could not be loaded | run cargo xtask fetch-pdfium or set LAMINA_PDF_PDFIUM_DIR to the folder holding the PDFium library |
Facade errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
cancelled | cancelled | a CancelToken stopped the work; the job is finished and returned no partial result | nothing to do: this is the outcome the caller asked for; start a new job to try again |
job-finished | state | a job was stepped after it had finished, failed or been cancelled | stop stepping a job once a step returned its result or an error; create a new job |
document-changed | state | the document a job was created for changed (an edit, undo, redo, save or rebase) or a different document was passed to step | pass the document the job was created from, and don't change it while the job runs; create a new job |
Binding errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
builder-finished | state | a builder or one of its pages was used after it finished | create a new builder or page: a finished one takes no more drawing, pages, fonts or settings |
disposed | state | a document, builder or other handle was used after free(), or after the using block that held it ended | free a handle only after its last use; to keep working, open the document again or create a new builder |
invalid-argument | usage | a value passed to a binding does not have the shape its type declares | pass the value the type declarations describe; the data field says what was wrong |
source-budget-exceeded | limit | a sparse source was given more bytes than its byte budget allows | raise the source's budget, or open the file with fewer bytes fetched (smaller blocks, no readahead) |
unknown-option | usage | an options record passed to a binding has a key Lamina PDF does not know | remove the key or correct its spelling; didYouMean lists the closest known names |
Package errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
already-initialized | state | init() or loadText() named a different source for a module that is already loaded or loading | call init and loadText with a source once, before the first load; later calls need no source |
extract-module-not-loaded | state | page.text() was called on a document opened in the base module, which has no text extraction | load the extract module (init with the extract option, or loadExtract) or the text module before opening the document |
fetch-failed | io | a range source could not fetch input bytes: a network error, an HTTP status other than 200 and 206, a 206 whose Content-Range or length doesn't match the request, or a read that returned the wrong number of bytes | check that the URL is reachable and that the server sends the CORS headers range requests need (Content-Range must be exposed); data.reason and data.status say what failed |
not-initialized | state | an @laminapdf/sdk function was called before init() finished loading the WebAssembly module | await init() once before calling anything else in @laminapdf/sdk |
source-changed | io | a remote file changed while it was being read: its ETag or length differs from the first response | open the file again; the document already read parts of the old version and can't mix in the new one |
text-module-not-loaded | state | a font or text call was made on a builder of the base module, which has no fonts or text | load the text module with init and the text option, or with loadText, before creating the builder that draws text |
worker-version-mismatch | usage | the worker answered with a different protocol or package version than the client | serve the worker file from the same @laminapdf/sdk build as the page; don't load it from a CDN or an older deploy |
worker-crashed | internal | the worker running Lamina PDF stopped (an uncaught error, a WebAssembly trap or out of memory); calls in flight were rejected | call client.restart() and open the document again; report the file if it keeps happening |
worker-failed | internal | the worker client failed with an error that isn't Lamina PDF's (a bug in @laminapdf/sdk/worker, or a result it couldn't send) | this is a bug in Lamina PDF: report it with the call and the message; the worker keeps running |
wasm-trap | internal | the WebAssembly module trapped (a bug in Lamina PDF), so its instance was discarded and the next call gets a fresh one | open the document again and retry; report the file and the call if it traps again |
out-of-memory | limit | the WebAssembly module ran out of memory (its maximum, or what the engine could give), so its instance was discarded and the next call gets a fresh one | open fewer or smaller documents at once and free each when done; lower the open limits so hostile files fail sooner |
instance-reset | state | a handle was used after its WebAssembly instance was replaced, by reset() or after a trap | open the document again or create a new builder: nothing from the old instance survives |
Cli errors
| Code | Kind | What happened | What to do |
|---|---|---|---|
invalid-command-line | usage | the command line doesn't parse: an unknown command or flag, a missing argument, a bad value, or stdin named twice | run the command with --help to see its arguments |
invalid-page-range | usage | a page range doesn't fit the grammar, or names a page the document lacks | write pages as 1-5, z for the last page, r2 for the second from the end, or x3 to leave page 3 out; data.position is the character where the range went wrong |
binary-output-to-terminal | usage | the command would write a PDF to standard output, and standard output is a terminal | redirect standard output to a file or a pipe, or write to a file with -o |
unknown-code | usage | explain was given a code that isn't a Lamina PDF error or warning code | check the spelling against didYouMean, or list every code at the docs URL |
overwrite-refused | state | the output file exists, or the command would modify its input in place, and --force wasn't given | pass --force, or write to a new file with -o |
input-unreadable | io | an input file, standard input or the password file couldn't be read | check that the file exists and that you can read it; data.input names it |
output-unwritable | io | the output couldn't be written: its folder is missing or read-only, the disk is full, or standard output closed | check that the output's folder exists, that you can write to it and that the disk has room |
path-outside-roots | security | laminapdf mcp was given a path outside its --root folders, or a link that leads out of them | use a path inside one of the server's --root folders, or start the server with another --root |