aio-libs/aiohttp · error · TypeError
Unsupported type for last_modified
Error message
Unsupported type for last_modified: {type(value).__name__} What it means
Raised by the last_modified setter when the assigned value is not None, int, float, datetime.datetime, or str. The setter formats the header into an RFC 7231 IMF-fixdate string and only knows how to convert those five input shapes. Any other type (e.g. a date object, a pandas Timestamp, a tuple) is rejected as a programming error rather than silently mis-formatted.
Solutions
- Convert date to datetime before assigning: `resp.last_modified = datetime.datetime.combine(my_date, datetime.time.min)`.
- Convert to a Unix timestamp (int/float): `resp.last_modified = my_date.timestamp()` or `calendar.timegm(my_date.timetuple())`.
- Format as an RFC 7231 string yourself and assign the str form.
- If the model field is a pandas.Timestamp, call `.to_pydatetime()` to get a stdlib datetime.
Example fix
// before
resp.last_modified = record.updated_at.date() # date -> TypeError
// after
import datetime
resp.last_modified = datetime.datetime.combine(
record.updated_at.date(), datetime.time.min
) Defensive patterns
Strategy: type-guard
Validate before calling
import datetime
def coerce_last_modified(value):
if isinstance(value, datetime.date) and not isinstance(value, datetime.datetime):
return datetime.datetime.combine(value, datetime.time.min)
return value
resp.last_modified = coerce_last_modified(value) Type guard
import datetime
def is_supported_last_modified(value) -> bool:
return value is None or isinstance(value, (int, float, datetime.datetime, str)) Try / catch
try:
resp.last_modified = value
except TypeError:
resp.last_modified = str(value) # or convert to datetime/timestamp Prevention
- Use datetime.datetime, not datetime.date, for Last-Modified.
- For ORM DateField values, combine with time.min to get a datetime.
- Convert pandas.Timestamp via .to_pydatetime() before assigning.
- When in doubt, format to an RFC 7231 string and assign the str.
When it happens
Trigger: Assigning `resp.last_modified = datetime.date.today()` (date, not datetime), a `pandas.Timestamp`, a `tuple`, a `time.struct_time`, or any custom object to the last_modified property. Passing such a value through a helper that forwards to the setter.
Common situations: Using `datetime.date` (no time component) instead of `datetime.datetime`; using ORM/model fields declared as `DateField` that return date objects; feeding a numpy/pandas timestamp from analytics code into a caching middleware that sets Last-Modified.
Related errors
- text argument must be str (%r)
- Unsupported etag type
- body and text are not allowed together
- Cannot call write() after write_eof()
- Cannot call write() before prepare()
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/f39cd71b4b15fbeb.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_response.py:279
self, value: int | float | datetime.datetime | str | None
) -> None:
if value is None:
self._headers.pop(hdrs.LAST_MODIFIED, None)
elif isinstance(value, (int, float)):
self._headers[hdrs.LAST_MODIFIED] = time.strftime(
"%a, %d %b %Y %H:%M:%S GMT", time.gmtime(math.ceil(value))
)
elif isinstance(value, datetime.datetime):
if value.microsecond:
value = value.replace(microsecond=0) + datetime.timedelta(seconds=1)
self._headers[hdrs.LAST_MODIFIED] = time.strftime(
"%a, %d %b %Y %H:%M:%S GMT", value.utctimetuple()
)
elif isinstance(value, str):
self._headers[hdrs.LAST_MODIFIED] = value
else:
msg = f"Unsupported type for last_modified: {type(value).__name__}" # type: ignore[unreachable]
raise TypeError(msg)
@property
def etag(self) -> ETag | None:
quoted_value = self._headers.get(hdrs.ETAG)
if not quoted_value:
return None
elif quoted_value == ETAG_ANY:
return ETag(value=ETAG_ANY)
match = QUOTED_ETAG_RE.fullmatch(quoted_value)
if not match:
return None
is_weak, value = match.group(1, 2)
return ETag(
is_weak=bool(is_weak),
value=value,
)
@etag.setterView on GitHub (pinned to d041d4d0fd)