{"record":{"id":"e0a6077389b0365f","repo":"redis/redis-py","slug":"bad-query","errorCode":null,"errorMessage":"Bad query","messagePattern":"Bad query","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"redis/commands/search/commands.py","lineNumber":1449,"sourceCode":"        Issue an aggregation query.\n\n        ### Parameters\n\n        **query**: This can be either an `AggregateRequest`, or a `Cursor`\n\n        An `AggregateResult` object is returned. You can access the rows from\n        its `rows` property, which will always yield the rows of the result.\n\n        For more information see `FT.AGGREGATE <https://redis.io/commands/ft.aggregate>`_.\n        \"\"\"  # noqa\n        if isinstance(query, AggregateRequest):\n            has_cursor = bool(query._cursor)\n            cmd = [AGGREGATE_CMD, self.index_name] + query.build_args()\n        elif isinstance(query, Cursor):\n            has_cursor = True\n            cmd = [CURSOR_CMD, \"READ\", self.index_name] + query.build_args()\n        else:\n            raise ValueError(\"Bad query\", query)\n        cmd += self.get_params_args(query_params)\n\n        raw = self.execute_command(*cmd)\n        return self._parse_results(\n            AGGREGATE_CMD, raw, query=query, has_cursor=has_cursor\n        )\n\n    def _get_aggregate_result(\n        self, raw: List, query: Union[AggregateRequest, Cursor], has_cursor: bool\n    ):\n        if has_cursor:\n            if isinstance(query, Cursor):\n                query.cid = raw[1]\n                cursor = query\n            else:\n                cursor = Cursor(raw[1])\n            raw = raw[0]\n        else:","sourceCodeStart":1431,"sourceCodeEnd":1467,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/commands/search/commands.py#L1431-L1467","documentation":"Raised by the sync aggregate() (redis/commands/search/commands.py:1449) as a ValueError when the query argument is neither an AggregateRequest nor a Cursor. FT.AGGREGATE requires one of these typed request objects; a raw string, dict, or Query (which belongs to search()) is rejected.","triggerScenarios":"Calling index.aggregate('SELECT *'), index.aggregate(some_query_object), index.aggregate(None), or index.aggregate({...}). A Query object is for FT.SEARCH, not FT.AGGREGATE.","commonSituations":"Confusing search Query with AggregateRequest, passing a raw RQL string, or forgetting to wrap the request. Note this is the sync path - the async mirror is error 355.","solutions":["Build an AggregateRequest and pass that: index.aggregate(AggregateRequest('*').group_by('@field', reducers...)).","For cursor-based paging, pass a Cursor object returned by a prior aggregation.","Use search() with a Query for non-aggregating queries."],"exampleFix":"# before\nindex.aggregate('@genre:{rock}')\n# after\nfrom redis.commands.search.aggregation import AggregateRequest, Asc\nindex.aggregate(AggregateRequest('*').group_by('@genre', Asc('@genre')))","handlingStrategy":"type-guard","validationCode":"from redis.commands.search.aggregation import AggregateRequest, Cursor\n\ndef safe_aggregate(index, query, query_params=None):\n    if not isinstance(query, (AggregateRequest, Cursor)):\n        raise TypeError('aggregate() requires an AggregateRequest or Cursor')\n    return index.aggregate(query, query_params=query_params)","typeGuard":"from redis.commands.search.aggregation import AggregateRequest, Cursor\n\ndef is_aggregate_query(q) -> bool:\n    return isinstance(q, (AggregateRequest, Cursor))","tryCatchPattern":"try:\n    index.aggregate(query)\nexcept ValueError as e:\n    if 'Bad query' in str(e):\n        raise TypeError('pass AggregateRequest or Cursor to aggregate()') from e\n    raise","preventionTips":["Always wrap aggregations in AggregateRequest(...).","Use search(Query(...)) for non-aggregating queries.","Do not pass a raw query string to aggregate()."],"tags":["search","aggregate","valueerror","redisearch","sync"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}