{"record":{"id":"e177c6e184b3d5ca","repo":"redis/redis-py","slug":"collect-sort-by-must-contain-at-least-one-field","errorCode":null,"errorMessage":"collect sort_by must contain at least one field","messagePattern":"collect sort_by must contain at least one field","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"redis/commands/search/reducers.py","lineNumber":253,"sourceCode":"            if not names or any(not n.strip() for n in names):\n                raise ValueError(\n                    \"collect fields must be '*' or a non-empty list of names\"\n                )\n            names = [_ensure_at_prefix(n) for n in names]\n            args += [\"FIELDS\", str(len(names))] + names\n\n        # DISTINCT (optional)\n        if distinct:\n            args += [\"DISTINCT\"]\n\n        # SORTBY (optional)\n        if sort_by is not None:\n            sort_fields = [sort_by] if isinstance(sort_by, (Asc, Desc)) else sort_by\n            sort_args: list[str] = []\n            for f in sort_fields:\n                sort_args += [_ensure_at_prefix(f.field), f.DIRSTRING]\n            if not sort_args:\n                raise ValueError(\"collect sort_by must contain at least one field\")\n            args += [\"SORTBY\", str(len(sort_args))] + sort_args\n\n        # LIMIT (optional)\n        if limit is not None:\n            offset, count = limit\n            args += [\"LIMIT\", str(offset), str(count)]\n\n        super().__init__(*args)\n","sourceCodeStart":235,"sourceCodeEnd":262,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/commands/search/reducers.py#L235-L262","documentation":"Raised by the collect reducer constructor when sort_by is provided but resolves to zero sort fields (e.g. an empty list). COLLECT's SORTBY clause needs at least one Asc/Desc field; an empty sort list is a caller bug.","triggerScenarios":"Calling collect(sort_by=[]) or collect(sort_by=iter([])). Passing a filtered list that became empty.","commonSituations":"Building sort_by dynamically from user input that produced no fields. Mixing up Asc/Desc instances with raw strings so the list is structurally empty after processing.","solutions":["Omit sort_by entirely if no ordering is needed.","Pass at least one Asc or Desc instance: collect(sort_by=Desc('price')).","Guard dynamic lists: only set sort_by when it is non-empty."],"exampleFix":"// before\nr = collect(fields='*', sort_by=sort_list)  # sort_list == []\n// after\nfrom redis.commands.search.aggregation import Desc\nr = collect(fields='*', sort_by=sort_list or Desc('price'))\n# or omit it\nr = collect(fields='*')","handlingStrategy":"validation","validationCode":"sort_by = sort_by if (isinstance(sort_by, (Asc, Desc)) or (sort_by and len(list(sort_by)) > 0)) else None\ncollect(fields=fields, sort_by=sort_by)","typeGuard":null,"tryCatchPattern":"try:\n    r = collect(fields=fields, sort_by=sort_by)\nexcept ValueError:\n    r = collect(fields=fields)","preventionTips":["Omit sort_by entirely when no ordering is needed.","Build sort lists from concrete Asc/Desc instances."],"tags":["redisearch","aggregation","reducer","sort","argument-validation"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}