aio-libs/aiohttp · error · TypeError
Unable to decode - content not cached. Call as_bytes()…
Error message
Unable to decode - content not cached. Call as_bytes() first.
What it means
Raised by AsyncIterablePayload.decode when there are no cached chunks to decode. Because an async iterable is consumed lazily and can only be read once, decode() cannot materialize the content; the chunks must first be populated by awaiting as_bytes(), which caches them. Calling decode() before that has nothing to work with.
Solutions
- Await as_bytes() first to populate the cache, then call decode(): data = await payload.as_bytes(); text = payload.decode().
- If you only need the string once, use text = (await payload.as_bytes()).decode('utf-8') directly.
- Be aware that caching loads the entire body into memory; for large streams prefer incremental processing.
Example fix
# before text = payload.decode() # after await payload.as_bytes() # populates _cached_chunks text = payload.decode()
Defensive patterns
Strategy: validation
Validate before calling
if payload._cached_chunks is None:
await payload.as_bytes()
text = payload.decode() Type guard
def payload_is_cached(payload) -> bool:
return getattr(payload, '_cached_chunks', None) is not None Try / catch
try:
text = payload.decode()
except TypeError:
await payload.as_bytes()
text = payload.decode() Prevention
- Always await as_bytes() before decode() on streaming payloads.
- Prefer (await payload.as_bytes()).decode() for one-shot string access.
- Do not consume the underlying iterable directly if you later need decode().
When it happens
Trigger: Calling payload.decode() on an AsyncIterablePayload that has never been awaited via as_bytes(); calling decode() after the iterable was consumed directly (e.g. async for chunk in payload) without going through as_bytes().
Common situations: Inspecting/debugging a streaming payload's body; reusing a payload after partial consumption; code that worked for BytesPayload.decode() reused on a streaming payload.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Unable to decode.
- Unable to read body part as bytes. Use write() to consume.
- () called while another coroutine is already waiting for…
- Unsupported order
- value argument must support collections.abc.AsyncIterable…
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/8c9d8667e471d70c.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/payload.py:1078
if remaining_bytes is None:
await writer.write(chunk)
# If we have a content length limit
elif remaining_bytes > 0:
await writer.write(chunk[:remaining_bytes])
remaining_bytes -= len(chunk)
# We still want to exhaust the iterator even
# if we have reached the content length limit
# since the file handle may not get closed by
# the iterator if we don't do this
except StopAsyncIteration:
# Iterator is exhausted
self._iter = None
def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str:
"""Decode the payload content as a string if cached chunks are available."""
if self._cached_chunks is not None:
return b"".join(self._cached_chunks).decode(encoding, errors)
raise TypeError("Unable to decode - content not cached. Call as_bytes() first.")
async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes:
"""
Return bytes representation of the value.
This method reads the entire async iterable content and returns it as bytes.
It generates and caches the chunks for future reuse.
"""
# If we have cached chunks, return them joined
if self._cached_chunks is not None:
return b"".join(self._cached_chunks)
# If iterator is exhausted and no cache, return empty
if self._iter is None:
return b""
# Read all chunks and cache them
chunks: list[bytes] = []View on GitHub (pinned to d041d4d0fd)