aio-libs/aiohttp · error · TypeError
Unsupported type for last_modified: {type(value).__name__}
Error message
Unsupported type for last_modified: {type(value).__name__} What it means
The last_modified setter on StreamResponse only accepts int, float (unix timestamps), datetime.datetime, str, or None. Any other type falls through to a TypeError. A plain datetime.date (without time) is NOT accepted and is the most common culprit.
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 c0ef574e29)
Solutions
- Convert date to datetime: resp.last_modified = datetime.datetime.combine(d, datetime.time.min).
- Pass a unix timestamp (int/float) or an HTTP-date string instead.
- If you already have a datetime.datetime, just assign it directly (microseconds are rounded up automatically).
Example fix
# before resp.last_modified = record.updated_at.date() # date, not datetime -> 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)) Prevention
- Check the type of ORM/dataframe timestamp columns before assignment.
- Remember datetime.date is NOT accepted; only datetime.datetime is.
- Pass unix timestamps (int/float) when in doubt — they always work.
When it happens
Trigger: Assigning `resp.last_modified = some_date` where some_date is a datetime.date (not datetime.datetime), a tuple, a pd.Timestamp without datetime inheritance, or any custom object. Intended types are listed in the isinstance checks at lines 265-276.
Common situations: Using a column from pandas/sql that yields date objects; passing a struct_time; forgetting to convert a date to datetime before assignment.
Related errors
- Unsupported etag type: {type(value)}. etag must be str, ETag
- Got more than {limit} bytes when reading: {line!r}.
- Setting charset for application/octet-stream doesn't make se
- charset must not be in content_type argument
- passing both Content-Type header and content_type or charset
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/f39cd71b4b15fbeb.json.
Report an issue: GitHub.