redis/redis-py · error · AttributeError
Did not receive a Filter object.
Error message
Did not receive a Filter object.
What it means
Raised by Query._get_args_tags (invoked via get_args()) when an entry in the query's internal _filters list is not an instance of the Filter base class (NumericFilter or GeoFilter). This guards the query builder from emitting a malformed FILTER/GEOFILTER clause. Normally add_filter() is the only way to populate _filters, so this indicates direct manipulation or a non-Filter object slipped through add_filter.
Source
Thrown at redis/commands/search/query.py:213
args += ["LIMIT", self._offset, self._num]
return args
def _get_args_tags(self) -> List[Union[str, int, float]]:
args: List[Union[str, int, float]] = []
if self._no_content:
args.append("NOCONTENT")
if self._fields:
args.append("INFIELDS")
args.append(len(self._fields))
args += self._fields
if self._verbatim:
args.append("VERBATIM")
if self._no_stopwords:
args.append("NOSTOPWORDS")
if self._filters:
for flt in self._filters:
if not isinstance(flt, Filter):
raise AttributeError("Did not receive a Filter object.")
args += flt.args
if self._with_payloads:
args.append("WITHPAYLOADS")
if self._scorer:
args += ["SCORER", self._scorer]
if self._with_scores:
args.append("WITHSCORES")
if self._ids:
args.append("INKEYS")
args.append(len(self._ids))
args += self._ids
if self._slop >= 0:
args += ["SLOP", self._slop]
if self._timeout is not None:
args += ["TIMEOUT", self._timeout]
if self._in_order:
args.append("INORDER")
if self._return_fields:View on GitHub (pinned to da03cdc7e8)
Solutions
- Wrap the field in a NumericFilter or GeoFilter: from redis.commands.search.query import NumericFilter, GeoFilter; query.add_filter(NumericFilter('price', 0, 100)).
- Stop mutating query._filters directly; only use add_filter().
- If you need a text/expression filter, put it in the query string itself, not in add_filter().
Example fix
// before
query.add_filter('price')
// after
from redis.commands.search.query import NumericFilter
query.add_filter(NumericFilter('price', 0, 100)) Defensive patterns
Strategy: type-guard
Validate before calling
from redis.commands.search.query import Filter
def add_safe_filter(query, flt):
if not isinstance(flt, Filter):
raise TypeError("filter must be a Filter instance")
query.add_filter(flt) Type guard
from redis.commands.search.query import Filter
def is_filter(val) -> bool:
return isinstance(val, Filter) Try / catch
try:
query.add_filter(candidate)
query.get_args()
except AttributeError:
# candidate was not a Filter; build one explicitly
pass Prevention
- Only pass NumericFilter or GeoFilter to add_filter().
- Never assign to query._filters directly.
- Put field-name/expression filters in the query string, not add_filter().
When it happens
Trigger: Calling query.add_filter('price') (passing a field name string instead of a NumericFilter/GeoFilter), or directly assigning query._filters = ['something'], then triggering argument serialization (e.g. via client.ft().search(query)).
Common situations: Assuming add_filter takes a field name or a raw expression string instead of a Filter object; migrating code from a hand-built query string; third-party wrappers that inject into _filters.
Related errors
- Did not receive a SortByField.
- index_type must be one of {list(IndexType)}
- prefix must be provided
- At least one tag must be specified
- collect fields must be '*' or a non-empty list of names
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/1a4861e89e6028a9.json.
Report an issue: GitHub.