RyanCodrai/turbovec · error · TypeError

Both metadata value and filter value must be strings for the

Error message

Both metadata value and filter value must be strings for the TEXT_MATCH operator

What it means

TEXT_MATCH requires both the metadata value and the filter value to be strings so substring semantics are well-defined. The reference implementation raises AttributeError on non-strings (llama_index issue #302); this library raises a clearer TypeError instead.

Source

Thrown at turbovec-python/python/turbovec/llama_index.py:764

            return value >= target
        if op == FilterOperator.LTE:
            return value <= target
        if op == FilterOperator.IN:
            return value in target
        if op == FilterOperator.NIN:
            return value not in target
        if op == FilterOperator.CONTAINS:
            return target in value
        if op == FilterOperator.TEXT_MATCH:
            # Case-SENSITIVE substring. `FilterOperator` defines
            # TEXT_MATCH and TEXT_MATCH_INSENSITIVE as distinct operators,
            # so folding case here would collapse that distinction and
            # leave no way to ask for a case-sensitive match. The type
            # guard is ours: the reference raises AttributeError on a
            # non-string (issue #302).
            if isinstance(target, str) and isinstance(value, str):
                return target in value
            raise TypeError(
                "Both metadata value and filter value must be strings "
                "for the TEXT_MATCH operator"
            )
        if _TEXT_MATCH_INSENSITIVE is not None and op == _TEXT_MATCH_INSENSITIVE:
            if isinstance(target, str) and isinstance(value, str):
                return target.lower() in value.lower()
            raise TypeError(
                "Both metadata value and filter value must be strings "
                "for the TEXT_MATCH_INSENSITIVE operator"
            )
        if op == FilterOperator.ALL:
            # Reference (`utils.py:152-153`): every element of `target`
            # must be present in the metadata value (which is typically
            # a list — tag-set matching).
            return all(t in value for t in target)
        if op == FilterOperator.ANY:
            return any(t in value for t in target)
        raise NotImplementedError(

View on GitHub (pinned to ccab9f325e)

Solutions

  1. Convert the metadata field to a string at ingestion time (store it as str)
  2. Convert the filter value to a string: FilterValue=str(123)
  3. Use a numeric operator (EQ/GT) instead of TEXT_MATCH for non-string fields

Example fix

// before
MetadataFilter(key="count", value=5, operator=FilterOperator.TEXT_MATCH)
// after
MetadataFilter(key="count", value="5", operator=FilterOperator.TEXT_MATCH)
Defensive patterns

Strategy: type-guard

Validate before calling

if op == FilterOperator.TEXT_MATCH and not (isinstance(meta_val, str) and isinstance(filt_val, str)):
    raise TypeError("TEXT_MATCH requires string metadata and filter values")

Type guard

def text_match_safe(meta_val, filt_val) -> bool:
    return isinstance(meta_val, str) and isinstance(filt_val, str) and meta_val in filt_val

Try / catch

try:
    store.query(q)
except TypeError as e:
    if "TEXT_MATCH operator" in str(e):
        coerce_filter_values_to_str(q.filters)
        store.query(q)
    else:
        raise

Prevention

When it happens

Trigger: Querying with FilterOperator.TEXT_MATCH where the stored metadata field is a number/bool/list or the filter value is a non-string.

Common situations: Numeric fields (ids, counts) queried with TEXT_MATCH; forgetting str() conversion on the filter value; metadata schema drift after a pipeline change.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of RyanCodrai/turbovec@ccab9f325e (2026-09-06). Data as JSON: /api/errors/bff3a80755c93314. Report an issue: GitHub.