celery/celery · error · InvalidTaskError
invalid expires value {expires!r}: {exc}
Error message
invalid expires value {expires!r}: {exc} What it means
Symmetric to eta: the message's expires field is parsed with maybe_iso8601; on AttributeError/ValueError/TypeError, Request raises InvalidTaskError('invalid expires value {expires!r}: {exc}'). expires may be a datetime or a seconds-int, but the ISO parse path fails on malformed values.
Source
Thrown at celery/worker/request.py:151
# timezone means the message is timezone-aware, and the only timezone
# supported at this point is UTC.
eta = self._request_dict.get('eta')
if eta is not None:
try:
eta = maybe_iso8601(eta)
except (AttributeError, ValueError, TypeError) as exc:
raise InvalidTaskError(
f'invalid ETA value {eta!r}: {exc}')
self._eta = maybe_make_aware(eta, self.tzlocal)
else:
self._eta = None
expires = self._request_dict.get('expires')
if expires is not None:
try:
expires = maybe_iso8601(expires)
except (AttributeError, ValueError, TypeError) as exc:
raise InvalidTaskError(
f'invalid expires value {expires!r}: {exc}')
self._expires = maybe_make_aware(expires, self.tzlocal)
else:
self._expires = None
delivery_info = message.delivery_info or {}
properties = message.properties or {}
self._delivery_info = {
'exchange': delivery_info.get('exchange'),
'routing_key': delivery_info.get('routing_key'),
'priority': properties.get('priority'),
'redelivered': delivery_info.get('redelivered', False),
}
self._request_dict.update({
'properties': properties,
'reply_to': properties.get('reply_to'),
'correlation_id': properties.get('correlation_id'),
'hostname': self._hostname,View on GitHub (pinned to 571efe8120)
Solutions
- Pass expires as an int/float number of seconds (relative) or a timezone-aware datetime / ISO-8601 string.
- Format datetimes with .isoformat() on the producer side.
- Validate the value type before sending and avoid locale-formatted strings.
Example fix
// before task.apply_async(expires='in 5 minutes') # InvalidTaskError // after task.apply_async(expires=300) # 300 seconds # or task.apply_async(expires=datetime(2026,8,4,10,15,tzinfo=timezone.utc))
Defensive patterns
Strategy: validation
Validate before calling
from numbers import Real
from datetime import datetime
def valid_expires(value):
if isinstance(value, Real) and not isinstance(value, bool):
return float(value)
if isinstance(value, datetime):
return value
raise ValueError(f'invalid expires value: {value!r}') Type guard
from numbers import Real
from datetime import datetime
def is_valid_expires(v) -> bool:
return (isinstance(v, Real) and not isinstance(v, bool)) or isinstance(v, datetime) Try / catch
from celery.exceptions import InvalidTaskError
try:
task.apply_async(expires=user_expires)
except InvalidTaskError as e:
if 'invalid expires' in str(e):
# fall back to a numeric seconds value
...
else:
raise Prevention
- Prefer expires as seconds (number) or an aware datetime.
- Validate producer-side expires before publishing.
- Do not reuse eta-formatted strings for expires.
When it happens
Trigger: A producer sends a task with apply_async(expires=...) as a malformed ISO string; passing a non-ISO/non-int value that reaches the ISO parser path.
Common situations: Confusing expires (relative seconds vs absolute datetime); passing a localized date string; a third-party producer with the wrong format; copying eta-style strings into expires.
Related errors
- invalid ETA value {eta!r}: {exc}
- Message does not have args/kwargs
- Task keyword arguments must be a mapping
- task args must be a list or tuple
- task keyword arguments must be a mapping
AI-assisted analysis of celery/celery@571efe8120 (2026-08-04).
Data as JSON: /data/errors/bfd0b34620b6245a.json.
Report an issue: GitHub.