{"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/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/commands/search/commands.py#L1431-L1467","documentation":"Raised by the sync aggregate() when the query argument is neither an AggregateRequest nor a Cursor. Unlike search(), aggregate() does NOT accept a plain string — you must wrap your query in an AggregateRequest. The error is raised with two args (\"Bad query\", query) so str(exc) shows both.","triggerScenarios":"Call client.aggregate('some query string') or client.aggregate(123) — passing anything other than AggregateRequest(...) or Cursor(...).","commonSituations":"Assuming aggregate() accepts a string like search() does; passing a Query object (wrong type — Query is for search/explain); passing a raw cursor id instead of a Cursor object.","solutions":["Wrap your query: client.aggregate(AggregateRequest('*').group_by(...)).","For cursor reads, pass a Cursor object returned by a previous aggregate().","Use client.search(Query(...)) if you wanted a plain search rather than aggregation."],"exampleFix":"// before\nclient.aggregate('@title:hello')\n// after\nfrom redis.commands.search.aggregation import AggregateRequest\nclient.aggregate(AggregateRequest('@title:hello'))","handlingStrategy":"type-guard","validationCode":"from redis.commands.search.aggregation import AggregateRequest, Cursor\n\ndef safe_aggregate(client, query, query_params=None):\n    if isinstance(query, str):\n        query = AggregateRequest(query)\n    if not isinstance(query, (AggregateRequest, Cursor)):\n        raise TypeError(f\"query must be AggregateRequest/Cursor, got {type(query)}\")\n    return client.aggregate(query, query_params=query_params)","typeGuard":"from redis.commands.search.aggregation import AggregateRequest, Cursor\n\ndef is_aggregate_query(v) -> bool:\n    return isinstance(v, (AggregateRequest, Cursor))","tryCatchPattern":"try:\n    client.aggregate(query)\nexcept ValueError as e:\n    if \"Bad query\" in str(e):\n        from redis.commands.search.aggregation import AggregateRequest\n        client.aggregate(AggregateRequest(str(query)))\n    else:\n        raise","preventionTips":["aggregate() never accepts a plain string — always wrap in AggregateRequest.","Distinguish search() (accepts str/Query) from aggregate() (AggregateRequest/Cursor).","Keep a factory that returns the right wrapper type for each API."],"tags":["redis-search","validation","aggregate","valueerror"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}