Summary
packages/ragmir-core/src/parsing.ts:686 calls await pdf.destroy() unconditionally inside a finally block. Under unpdf@1.8.0 (the latest released version) this throws pdf.destroy is not a function and breaks PDF ingestion.
Repro
- Clone this repo and run
pnpm install. "unpdf": "^1.4.0" in packages/ragmir-core/package.json resolves to 1.8.0.
- Add a PDF to a project:
mkdir -p /tmp/repro/sources
cp some.pdf /tmp/repro/sources/
mkdir -p /tmp/repro/.ragmir
printf '{"version":"1.0","sources":["sources/**/*.pdf"]}\n' > /tmp/repro/.ragmir/config.json
- Run
rgr ingest inside /tmp/repro.
- Observe (from a runtime trace):
Parsing: pdf.destroy is not a function
Expected
PDF parsing succeeds. The finally block should clean up safely even if pdf.destroy is missing in the current unpdf release.
Root cause
packages/ragmir-core/package.json declares "unpdf": "^1.4.0".
- In
unpdf@1.6.2, PDFDocumentProxy.destroy(): Promise<void> exists. Verified at runtime:
typeof pdf.destroy === 'function' // true
await pdf.destroy() // resolves cleanly
- In
unpdf@1.8.0, .destroy() was removed from the proxy. Verified at runtime against the same getDocumentProxy(...) call:
typeof pdf.destroy === 'undefined' // true
await pdf.destroy() // THROWS: pdf.destroy is not a function
There is no replacement method on the returned proxy in 1.8.0 (the prototype still exposes numPages, getPage, getAttachments, but no destroy, cleanup, or close).
The caret range in ^1.4.0 allowed this breaking bump into a release that silently removed the only cleanup API the parser relies on.
Proposed fixes
Pick one (or both):
Option A — Pin the dependency (smallest blast radius):
- "unpdf": "^1.4.0",
+ "unpdf": "~1.6.2",
Keeps pdf.destroy() working without code changes. Revisit when unpdf ships a replacement cleanup API.
Option B — Defensive guard in the parser (forward-compatible):
} finally {
- await pdf.destroy()
+ if (typeof pdf.destroy === "function") {
+ await pdf.destroy()
+ }
}
Robust against future API churn in unpdf / pdfjs-dist.
I recommend B + A together: the pin prevents surprise breakage for users running fresh pnpm install, and the guard makes the parser correct against any future API shape (and against any current build where unpdf has been replaced or bundled differently).
Environment
@jcode.labs/ragmir 4.4.0
unpdf 1.8.0 (auto-resolved from ^1.4.0)
- Node 22.23.1
pdfjs-dist bundled transitively inside unpdf 1.8.0
Workaround
A one-line patch against the installed dist/parsing.js makes ingest pass end-to-end on 4.4.0:
- await pdf.destroy();
+ if (typeof pdf.destroy === "function") await pdf.destroy();
After this patch, rgr ingest against a real PDF runs clean (no pdf.destroy is not a function, exit 0).
Note for maintainers
If unpdf has intentionally removed destroy() because the proxy is now GC-managed, the parser's finally block can simply be removed entirely — there is no observable leak from leaving the document proxy unreferenced. That decision should be made consciously, not by accident via a dependency bump.
Summary
packages/ragmir-core/src/parsing.ts:686callsawait pdf.destroy()unconditionally inside afinallyblock. Underunpdf@1.8.0(the latest released version) this throwspdf.destroy is not a functionand breaks PDF ingestion.Repro
pnpm install."unpdf": "^1.4.0"inpackages/ragmir-core/package.jsonresolves to1.8.0.rgr ingestinside/tmp/repro.Expected
PDF parsing succeeds. The
finallyblock should clean up safely even ifpdf.destroyis missing in the current unpdf release.Root cause
packages/ragmir-core/package.jsondeclares"unpdf": "^1.4.0".unpdf@1.6.2,PDFDocumentProxy.destroy(): Promise<void>exists. Verified at runtime:unpdf@1.8.0,.destroy()was removed from the proxy. Verified at runtime against the samegetDocumentProxy(...)call:numPages,getPage,getAttachments, but nodestroy,cleanup, orclose).The caret range in
^1.4.0allowed this breaking bump into a release that silently removed the only cleanup API the parser relies on.Proposed fixes
Pick one (or both):
Option A — Pin the dependency (smallest blast radius):
Keeps
pdf.destroy()working without code changes. Revisit when unpdf ships a replacement cleanup API.Option B — Defensive guard in the parser (forward-compatible):
} finally { - await pdf.destroy() + if (typeof pdf.destroy === "function") { + await pdf.destroy() + } }Robust against future API churn in unpdf / pdfjs-dist.
I recommend B + A together: the pin prevents surprise breakage for users running fresh
pnpm install, and the guard makes the parser correct against any future API shape (and against any current build where unpdf has been replaced or bundled differently).Environment
@jcode.labs/ragmir4.4.0unpdf1.8.0 (auto-resolved from^1.4.0)pdfjs-distbundled transitively inside unpdf 1.8.0Workaround
A one-line patch against the installed
dist/parsing.jsmakes ingest pass end-to-end on 4.4.0:After this patch,
rgr ingestagainst a real PDF runs clean (nopdf.destroy is not a function, exit 0).Note for maintainers
If unpdf has intentionally removed
destroy()because the proxy is now GC-managed, the parser'sfinallyblock can simply be removed entirely — there is no observable leak from leaving the document proxy unreferenced. That decision should be made consciously, not by accident via a dependency bump.