redis/redis-py · error · ValueError

Bad query type {type(query)}

Error message

Bad query type {type(query)}

What it means

Raised by _mk_query_args() (used by search(), explain(), etc.) when the query argument is neither a str nor a Query object. Strings are auto-wrapped in Query(); any other type is rejected. The library refuses to guess how to serialize arbitrary objects into a RediSearch query string.

Source

Thrown at redis/commands/search/commands.py:1309

        args = []
        if len(query_params) > 0:
            args.append("PARAMS")
            args.append(len(query_params) * 2)
            for key, value in query_params.items():
                args.append(key)
                args.append(value)
        return args

    def _mk_query_args(
        self, query, query_params: Optional[Dict[str, Union[str, int, float, bytes]]]
    ):
        args = [self.index_name]

        if isinstance(query, str):
            # convert the query from a text to a query object
            query = Query(query)
        if not isinstance(query, Query):
            raise ValueError(f"Bad query type {type(query)}")

        args += query.get_args()
        args += self.get_params_args(query_params)

        return args, query

    def search(
        self,
        query: Union[str, Query],
        query_params: Union[Dict[str, Union[str, int, float, bytes]], None] = None,
    ):
        """
        Search the index for a given query, and return a result of documents

        ### Parameters

        - **query**: the search query. Either a text for simple queries with
                     default parameters, or a Query object for complex queries.

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Pass a query string (e.g. '@title:hello') or a Query object.
  2. Build a Query: from redis.commands.search import Query; Query('@title:hello').
  3. For aggregation-style requests, use client.aggregate(AggregateRequest(...)) instead.

Example fix

// before
client.search({'title': 'hello'})
// after
client.search('@title:hello')
// or
client.search(Query('@title:hello').paging(0, 10))
Defensive patterns

Strategy: type-guard

Validate before calling

from redis.commands.search import Query

def safe_search(client, query, query_params=None):
    if isinstance(query, str):
        query = Query(query)
    elif not isinstance(query, Query):
        raise TypeError(f"query must be str or Query, got {type(query)}")
    return client.search(query, query_params=query_params)

Type guard

from redis.commands.search import Query

def is_search_query(v) -> bool:
    return isinstance(v, (str, Query))

Try / catch

try:
    client.search(query)
except ValueError as e:
    if "Bad query type" in str(e):
        client.search(Query(str(query)))
    else:
        raise

Prevention

When it happens

Trigger: Call client.search(query) where query is an int, dict, list, None, or an arbitrary object that is not a Query instance and not a string.

Common situations: Passing a parsed JSON object or dict as the query; passing None by mistake; passing an AggregateRequest (wrong API — that goes to aggregate()).

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/128b6c4212cc708d.json. Report an issue: GitHub.