mem0ai/mem0 · error · ValueError
Invalid datetime value in range filter for field '{key}': {e
Error message
Invalid datetime value in range filter for field '{key}': {e} What it means
Raised when Qdrant's filter builder detects a datetime-shaped range (values that look like dates/timestamps) but the upstream DatetimeRange constructor rejects the values — typically strings that are not valid RFC 3339 / ISO 8601 timestamps or non-numeric types. The original ValueError/TypeError from Qdrant is chained (`from e`) so the underlying parse failure is preserved in the message.
Source
Thrown at mem0/vector_stores/qdrant.py:307
return FieldCondition(key=key, match=MatchValue(value=value))
ops = set(value.keys())
range_ops = {"gt", "gte", "lt", "lte"}
non_range_ops = ops - range_ops
if ops & range_ops:
if non_range_ops:
raise ValueError(
f"Cannot mix range operators ({ops & range_ops}) with "
f"non-range operators ({non_range_ops}) for field '{key}'. "
f"Use AND to combine them as separate conditions."
)
range_kwargs = {op: value[op] for op in range_ops if op in value}
if self._is_datetime_range(range_kwargs):
try:
return FieldCondition(key=key, range=DatetimeRange(**range_kwargs))
except (ValueError, TypeError) as e:
raise ValueError(
f"Invalid datetime value in range filter for field '{key}': {e}"
) from e
return FieldCondition(key=key, range=Range(**range_kwargs))
elif "eq" in value:
return FieldCondition(key=key, match=MatchValue(value=value["eq"]))
elif "ne" in value:
return FieldCondition(key=key, match=MatchExcept(**{"except": [value["ne"]]}))
elif "in" in value:
return FieldCondition(key=key, match=MatchAny(any=value["in"]))
elif "nin" in value:
return FieldCondition(key=key, match=MatchExcept(**{"except": value["nin"]}))
elif "contains" in value or "icontains" in value:
# MatchText: with a full-text index, tokenized matching (all words must appear).
# Without a full-text index, exact substring match.
op = "icontains" if "icontains" in value else "contains"
text = value[op]
if op == "icontains":
logger.debug(View on GitHub (pinned to 001c235229)
Solutions
- Normalize date inputs to ISO 8601 / RFC 3339 before filtering: `datetime.isoformat()` or `dateutil.parser.parse(raw).isoformat()`.
- If the field is actually numeric (unix epoch), pass ints/floats so `_is_datetime_range` routes it to the plain Range constructor instead of DatetimeRange.
- Upgrade qdrant-client — DatetimeRange parsing strictness has changed between versions; align client and server versions.
Example fix
# before
filters = {"created_at": {"gte": "01/02/2024"}}
# after
from datetime import datetime, timezone
from dateutil import parser
def to_iso(raw: str) -> str:
return parser.parse(raw).astimezone(timezone.utc).isoformat()
filters = {"created_at": {"gte": to_iso("2024-01-02")}} Defensive patterns
Strategy: validation
Validate before calling
from datetime import datetime
def iso_or_none(raw) -> str | None:
if raw is None:
return None
dt = datetime.fromisoformat(str(raw).replace("Z", "+00:00")) # raises on garbage
return dt.isoformat()
filters = {"created_at": {"gte": iso_or_none(user_input)}} Type guard
def is_parseable_datetime(v) -> bool:
if isinstance(v, (int, float)) and not isinstance(v, bool):
return True # epoch values go to plain Range
if not isinstance(v, str):
return False
try:
datetime.fromisoformat(v.replace("Z", "+00:00"))
return True
except ValueError:
return False Try / catch
try:
results = memory.search("q", filters=filters)
except ValueError as e:
if "Invalid datetime value" in str(e):
raise ValueError(f"Bad date filter from caller: {filters}; expected ISO 8601") from e
raise Prevention
- Always serialize datetimes with datetime.isoformat() at the boundary; never forward free-text dates.
- Decide once per field whether it stores ISO strings or epoch numbers, and validate accordingly.
- Add a request-level schema check that datetime filter values parse before they reach the vector layer.
When it happens
Trigger: Filters such as `{"created_at": {"gte": "yesterday"}}`, `{"created_at": {"lt": "2024-13-45"}}`, `{"created_at": {"gte": "01/02/2024"}}` (slash format), or passing None/bool where a timestamp is expected — any value DatetimeRange cannot parse.
Common situations: Free-form user input used directly as a date filter; locale-specific date formats (MM/DD/YYYY vs ISO); LLM tool-calls emitting relative dates like 'last week' instead of ISO strings; storing timestamps with a 'Z' suffix or offset that the installed qdrant-client version rejects.
Related errors
- Cannot mix range operators ({ops & range_ops}) with non-rang
- Unsupported filter operator(s) for field '{key}': {ops}. Sup
- {key} filter value must be a list of filter dicts, got {type
- {key} filter list item at index {i} must be a dict, got {typ
- AND filter value must be a list of filter dicts, got ${typeo
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/e37a78b0004d9b8d.
Report an issue: GitHub.