fix: strip NUL bytes from document metadata before pgvector insert#296
Open
nangelovv wants to merge 1 commit into
Open
fix: strip NUL bytes from document metadata before pgvector insert#296nangelovv wants to merge 1 commit into
nangelovv wants to merge 1 commit into
Conversation
PDFs produced by some drivers (e.g. Canon / Adobe PSL PostScript) pad metadata strings such as `producer` with a trailing NUL byte (U+0000). PostgreSQL cannot store U+0000 in text/JSONB columns, so the entire multi-row batch INSERT into langchain_pg_embedding is rejected and rolled back: the file silently fails to embed (and the provider embedding cost is incurred then discarded). rag_api already strips NUL from document *text* via clean_text/remove_null (the LibreChat Discussion #2243 fix for Google-Docs PDFs), but metadata was merged into the JSONB cmetadata column unsanitized. This extends the same protection to metadata fields, which JSONB rejects identically. - Add clean_metadata() in app/utils/document_loader.py: recursively strips NUL from all string values (including nested dict/list), reusing remove_null. - Apply it unconditionally at the metadata merge in _prepare_documents_sync (the single choke point both insert paths flow through). Not gated behind the PDF-only clean_content flag, since the JSONB NUL constraint holds for every loader and file type. - Add unit tests covering producer NUL, nested dict/list values, and no over-stripping of non-string/clean values.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Strip NUL bytes (
\u0000) from document metadata before it reaches thepgvector
cmetadataJSONB column, so PDFs with a NUL in fields likeproducercan be embedded instead of failing the whole batch insert.
Fixes #295.
Why
cmetadatais JSONB and PostgreSQL cannot store\u0000in text/JSONB.PDFs produced by Canon / Adobe PSL PostScript drivers (and some Google-Docs
exports) pad
producer/creator/titlewith a trailing NUL. The unsanitizedmetadata merge in
_prepare_documents_synclets that NUL flow into the insert,which PostgreSQL rejects with:
The entire multi-row batch INSERT is rolled back, so the file is silently never
stored or searchable (and the embedding provider cost is already spent).
rag_api already strips NUL from document text via
clean_text/remove_null(the LibreChat Discussion #2243 fix). This PR extends the sameprotection to metadata fields, which JSONB rejects identically.
Changes
app/utils/document_loader.py: addclean_metadata()— recursively stripsNUL from all string values (including nested dict/list), reusing
remove_null.app/routes/document_routes.py: applyclean_metadata()at the metadatamerge in
_prepare_documents_sync(the single choke point both the asyncpipeline and batched-sync insert paths flow through). Applied
unconditionally, not gated behind the PDF-only
clean_contentflag,since the JSONB NUL constraint holds for every loader and file type. The
known-clean keys (
file_id/user_id/digest) stay outside thesanitizer.
tests/utils/test_document_loader.py: unit tests for producer NUL, nesteddict/list values, and no over-stripping of non-string/clean values.
Testing
tests/utils/test_document_loader.pysuite passes(25/25) in the project's Python 3.10 Docker image.
in
producernow embeds successfully (status: true) and is searchable viaPOST /query.original
UntranslatableCharacterfailure; with the change applied itsucceeds. A normal PDF still embeds with intact metadata (no over-stripping).
🤖 Generated with Claude Code