You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: PGVectorStore incorrectly handles string metadata that resembles numbers #15962
The PGVectorStore is incorrectly handling string metadata that resembles numbers, causing a DataError: invalid input syntax for type double precision when filtering. This occurs presumably in the _build_filter_clause:458 method, which attempts to cast string values to floats if they can be interpreted as numbers.
try:
return text(
f"(metadata_->>'{filter_.key}')::float "
f"{self._to_postgres_operator(filter_.operator)} "
f"{float(filter_.value)}"
)
except ValueError:
# If not a number, then treat it as a string
return text(
f"metadata_->>'{filter_.key}' "
f"{self._to_postgres_operator(filter_.operator)} "
f"'{filter_.value}'"
)
Workaround
As a temporary workaround, you can add non-numeric text to the file names (e.g., "file_2024_123" instead of "2024_123"). This prevents the value from being interpreted as a number and avoids the casting issue.
Version
0.11.8
Steps to Reproduce
Create a PGVectorStore instance
Add documents with metadata where f.e 'file_name' is a string that looks like a number (e.g., "2024_123")
Attempt to filter using this 'file_name'
Relevant Logs/Tracbacks
The filter operation fails with the error:
DataError: (psycopg2.errors.InvalidTextRepresentation) invalid input syntax fortype double precision: "2024_123"
[SQL: SELECT public.data_contract_hybrid_vector_store.id, public.data_contract_hybrid_vector_store.node_id, public.data_contract_hybrid_vector_store.text, public.data_contract_hybrid_vector_store.metadata_, public.data_contract_hybrid_vector_store.embedding <=> %(embedding_1)s AS distance FROM public.data_contract_hybrid_vector_store WHERE (metadata_->>'file_name')::float = 20240717150625.0 ORDER BY distance asc LIMIT %(param_1)s]
Hello @nicolas-werner! I'm here to help you with any bugs, questions, or contributions you may have. Let's work together to solve the issue you've encountered.
To fix the PGVectorStore to correctly handle string metadata that resembles numbers without causing a DataError: invalid input syntax for type double precision when filtering, you need to ensure that the metadata is treated as text in the SQL queries. This can be done by explicitly casting the metadata fields to text in the filter conditions.
Here is a modified version of the query method to handle this:
In this code, the cast_to_text_filter function ensures that all metadata values are cast to text, preventing the DataError caused by invalid input syntax for type double precision [1].
Hi, @nicolas-werner. I'm Dosu, and I'm helping the LlamaIndex team manage their backlog. I'm marking this issue as stale.
Issue Summary
The PGVectorStore was incorrectly handling string metadata resembling numbers, causing a DataError during filtering.
A solution was suggested to modify the query method to cast metadata fields to text in SQL queries.
An alternative solution was proposed to catch the DataError in an exception block.
The issue appears to be resolved with the modification of the query method.
Next Steps
Please confirm if this issue is still relevant to the latest version of the LlamaIndex repository. If so, you can keep the discussion open by commenting on the issue.
Otherwise, the issue will be automatically closed in 7 days.
Thank you for your understanding and contribution!
Still present, unchanged, on main: postgres/base.py#L737-L751. Neither of the suggestions in this thread will fix it, so it is worth naming the actual mechanism.
The try: float(...) is being used as a test for "can Postgres cast this to float8", and the two grammars are not the same. Python's float() accepts PEP 515 digit separators; Postgres does not:
That is exactly what your traceback shows. Running the real _build_filter_clause (source lifted from main, stub MetadataFilter) with FilterOperator.EQ:
value='2024_0717_150625' -> WHERE (metadata_->>'file_name')::float = 20240717150625.0
value='2024_123' -> WHERE (metadata_->>'file_name')::float = 2024123.0
value='report.pdf' -> WHERE metadata_->>'file_name' = 'report.pdf'
value="O'Brien" -> WHERE metadata_->>'author' = 'O'Brien'
value=2024 -> WHERE (metadata_->>'year')::float = 2024.0
The first line reproduces the = 20240717150625.0 in your posted SQL literally.
There is a second half to your error that is easy to miss and matters for the fix. The value Postgres rejected was "2024_123", but the value in the predicate was 20240717150625.0. So "2024_123" is not your filter value, it is some other row's stored file_name. (metadata_->>'file_name')::float is evaluated per row, so once any row in the table has a non-castable value under that key, the query fails no matter what you filter for. A single filename like report.pdf in the collection breaks every numeric-looking file_name filter.
That is also why @CharbelAD's suggestion of catching DataError cannot work: the exception surfaces from the server during execute(), after the full statement (usually an AND/OR tree of many clauses) has been built and sent. There is no clause left to retry, and no way to know which one to rebuild. Any fix has to happen at clause construction time.
The construction is also unsafe, which the O'Brien line above shows: filter_.value is interpolated straight into text(). A quote in a metadata value produces a syntax error, and anything else in that value is executed as SQL. In a RAG service where filters carry tenant ids, filenames, or anything else derived from user input, that is worth fixing on its own. The IN/NIN, ANY/ALL, CONTAINS and TEXT_MATCH branches interpolate the same way, and filter_.key is interpolated everywhere too.
Both problems go away with the same change: decide numeric versus text from the Python type of the value rather than by attempting to parse it, and bind instead of interpolate.
One behavior change to be deliberate about: MetadataFilter(key="year", value="2024", operator=GT) currently compares numerically and would start comparing as text (lexicographic). That is arguably the correct reading of a str value, and value=2024 still does the numeric thing, but it is a break for anyone who has been passing numbers as strings, so it belongs in a changelog entry rather than a silent patch.
Disclosure: I used Claude Code to help investigate and draft this comment. The float() table and the generated SQL above are real output; the SQL was produced by executing the current _build_filter_clause and _to_postgres_operator source from main against stub filter objects.
Taking a crack at this — the float() / ::float path on string metadata that looks numeric (e.g. "2024_123") is still present on main, and I'm planning a small fix in PGVectorStore._build_filter_clause with unit tests + a postgres integration version bump.
Noting draft #22367 already sketches the isinstance approach; it's been idle as a draft, so I'll open a ready-for-review PR in the same spirit. Happy to defer if that one is about to land.
Activity
dosubot commentedon Sep 11, 2024
Hello @nicolas-werner! I'm here to help you with any bugs, questions, or contributions you may have. Let's work together to solve the issue you've encountered.
To fix the
PGVectorStoreto correctly handle string metadata that resembles numbers without causing aDataError: invalid input syntax for type double precisionwhen filtering, you need to ensure that the metadata is treated as text in the SQL queries. This can be done by explicitly casting the metadata fields to text in the filter conditions.Here is a modified version of the
querymethod to handle this:In this code, the
cast_to_text_filterfunction ensures that all metadata values are cast to text, preventing theDataErrorcaused by invalid input syntax for type double precision [1].To continue talking to Dosu, mention @dosu.
CharbelAD commentedon Sep 15, 2024
Couldn't this issue be fixed by catching
DataErrorin theexcept?dosubot commentedon Apr 10, 2025
Hi, @nicolas-werner. I'm Dosu, and I'm helping the LlamaIndex team manage their backlog. I'm marking this issue as stale.
Issue Summary
PGVectorStorewas incorrectly handling string metadata resembling numbers, causing aDataErrorduring filtering.querymethod to cast metadata fields to text in SQL queries.DataErrorin an exception block.querymethod.Next Steps
Thank you for your understanding and contribution!
ErenAta16 commentedon Jul 22, 2026
Still present, unchanged, on
main: postgres/base.py#L737-L751. Neither of the suggestions in this thread will fix it, so it is worth naming the actual mechanism.The
try: float(...)is being used as a test for "can Postgres cast this tofloat8", and the two grammars are not the same. Python'sfloat()accepts PEP 515 digit separators; Postgres does not:That is exactly what your traceback shows. Running the real
_build_filter_clause(source lifted frommain, stubMetadataFilter) withFilterOperator.EQ:The first line reproduces the
= 20240717150625.0in your posted SQL literally.There is a second half to your error that is easy to miss and matters for the fix. The value Postgres rejected was
"2024_123", but the value in the predicate was20240717150625.0. So"2024_123"is not your filter value, it is some other row's storedfile_name.(metadata_->>'file_name')::floatis evaluated per row, so once any row in the table has a non-castable value under that key, the query fails no matter what you filter for. A single filename likereport.pdfin the collection breaks every numeric-lookingfile_namefilter.That is also why @CharbelAD's suggestion of catching
DataErrorcannot work: the exception surfaces from the server duringexecute(), after the full statement (usually anAND/ORtree of many clauses) has been built and sent. There is no clause left to retry, and no way to know which one to rebuild. Any fix has to happen at clause construction time.The construction is also unsafe, which the
O'Brienline above shows:filter_.valueis interpolated straight intotext(). A quote in a metadata value produces a syntax error, and anything else in that value is executed as SQL. In a RAG service where filters carry tenant ids, filenames, or anything else derived from user input, that is worth fixing on its own. TheIN/NIN,ANY/ALL,CONTAINSandTEXT_MATCHbranches interpolate the same way, andfilter_.keyis interpolated everywhere too.Both problems go away with the same change: decide numeric versus text from the Python type of the value rather than by attempting to parse it, and bind instead of interpolate.
One behavior change to be deliberate about:
MetadataFilter(key="year", value="2024", operator=GT)currently compares numerically and would start comparing as text (lexicographic). That is arguably the correct reading of astrvalue, andvalue=2024still does the numeric thing, but it is a break for anyone who has been passing numbers as strings, so it belongs in a changelog entry rather than a silent patch.Disclosure: I used Claude Code to help investigate and draft this comment. The
float()table and the generated SQL above are real output; the SQL was produced by executing the current_build_filter_clauseand_to_postgres_operatorsource frommainagainst stub filter objects.fix(pgvector): use type check for numeric filter values
kumarpriyanshu09 commentedon Sep 29, 2026
Taking a crack at this — the
float()/::floatpath on string metadata that looks numeric (e.g."2024_123") is still present onmain, and I'm planning a small fix inPGVectorStore._build_filter_clausewith unit tests + a postgres integration version bump.Noting draft #22367 already sketches the isinstance approach; it's been idle as a draft, so I'll open a ready-for-review PR in the same spirit. Happy to defer if that one is about to land.