redis/redis-py · error · ValueError

Must provide AggregateRequest object or Query object.

Error message

Must provide AggregateRequest object or Query object.

What it means

Raised by profile() when the query argument is neither an AggregateRequest nor a Query. profile() dispatches to FT.PROFILE with either 'AGGREGATE' or 'SEARCH' mode, so it must know which; a Cursor, string, or any other type is rejected.

Source

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

        **query_params**: Define one or more value parameters.
        Each parameter has a name and a value.

        """
        st = time.monotonic()
        cmd = [PROFILE_CMD, self.index_name, ""]
        if limited:
            cmd.append("LIMITED")
        cmd.append("QUERY")

        if isinstance(query, AggregateRequest):
            cmd[2] = "AGGREGATE"
            cmd += query.build_args()
        elif isinstance(query, Query):
            cmd[2] = "SEARCH"
            cmd += query.get_args()
            cmd += self.get_params_args(query_params)
        else:
            raise ValueError("Must provide AggregateRequest object or Query object.")

        res = self.execute_command(*cmd)

        return self._parse_results(
            PROFILE_CMD, res, query=query, duration=(time.monotonic() - st) * 1000.0
        )

    def spellcheck(self, query, distance=None, include=None, exclude=None):
        """
        Issue a spellcheck query

        Args:

            query: search query.
            distance: the maximal Levenshtein distance for spelling
                       suggestions (default: 1, max: 4).
            include: specifies an inclusion custom dictionary.
            exclude: specifies an exclusion custom dictionary.

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Pass a Query object for SEARCH profiling or an AggregateRequest for AGGREGATE profiling.
  2. Build the right wrapper: profile(Query('@x:1')) or profile(AggregateRequest('*')).
  3. If you only want the plan without timing, use explain() for searches.

Example fix

// before
client.profile('@title:hello')
// after
from redis.commands.search import Query
client.profile(Query('@title:hello'))
Defensive patterns

Strategy: type-guard

Validate before calling

from redis.commands.search import Query
from redis.commands.search.aggregation import AggregateRequest

def safe_profile(client, query, limited=False, query_params=None):
    if isinstance(query, str):
        raise TypeError("profile() needs a Query or AggregateRequest, not a string")
    if not isinstance(query, (Query, AggregateRequest)):
        raise TypeError(f"query must be Query/AggregateRequest, got {type(query)}")
    return client.profile(query, limited=limited, query_params=query_params)

Type guard

from redis.commands.search import Query
from redis.commands.search.aggregation import AggregateRequest

def is_profile_query(v) -> bool:
    return isinstance(v, (Query, AggregateRequest))

Try / catch

try:
    client.profile(query)
except ValueError as e:
    if "AggregateRequest object or Query object" in str(e):
        from redis.commands.search import Query
        client.profile(Query(str(query)))
    else:
        raise

Prevention

When it happens

Trigger: Call client.profile(query) where query is a string, a Cursor, an int, None, or any type other than AggregateRequest / Query.

Common situations: Passing a raw query string (profile requires the strongly-typed object so it can pick the mode); passing a Cursor (cursors are not profiled); mixing up profile() with search()/aggregate().

Related errors


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