BerriAI/litellm · error · TypeError

File content stream does not support sync iteration

Error message

File content stream does not support sync iteration

What it means

FileContentStreamingResponse wraps the raw provider iterator returned by file-content retrieval. __iter__ checks that the wrapped stream_iterator exposes __next__ (a sync iterator); if the provider returned an async-only iterator (it only has __anext__), sync iteration with `for chunk in response` raises TypeError.

Source

Thrown at litellm/files/streaming.py:49

        self.stream_iterator = stream_iterator
        self.file_id = file_id
        self.model = model
        self.custom_llm_provider = custom_llm_provider
        self.logging_obj = logging_obj
        self.standard_logging_object: StandardLoggingPayload | None = None
        self._hidden_params: dict[str, Any] = {}
        self._logging_completed = False
        self._close_completed = False
        self._start_time = (
            logging_obj.start_time
            if logging_obj is not None and getattr(logging_obj, "start_time", None)
            else datetime.datetime.now()
        )
        self._sync_hidden_params()

    def __iter__(self) -> "FileContentStreamingResponse":
        if not hasattr(self.stream_iterator, "__next__"):
            raise TypeError("File content stream does not support sync iteration")
        return self

    def __next__(self) -> bytes:
        if not hasattr(self.stream_iterator, "__next__"):
            raise TypeError("File content stream does not support sync iteration")

        try:
            return next(cast(Iterator[bytes], self.stream_iterator))
        except StopIteration:
            self._log_success_sync()
            raise
        except Exception as e:
            self._log_failure_sync(e)
            raise

    def __aiter__(self) -> "FileContentStreamingResponse":
        if not hasattr(self.stream_iterator, "__anext__"):
            raise TypeError("File content stream does not support async iteration")

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Iterate the same flavor you created with: use async iteration (`async for chunk in response`) for async responses
  2. If you need sync bytes, make the original file-content call through the sync path so the wrapper gets a sync iterator
  3. Read the whole body once via the provided aclose()/read-style helpers on the async response instead of forcing sync iteration

Example fix

# before
resp = await client.files.acontent(fid)  # async-backed stream
for chunk in resp:  # TypeError
    ...

# after
async for chunk in resp:
    ...
Defensive patterns

Strategy: type-guard

Validate before calling

if not hasattr(resp.stream_iterator, "__next__"):
    raise TypeError("response is async-only; use `async for`")

Type guard

def is_sync_stream(resp) -> bool:
    return hasattr(resp.stream_iterator, "__next__")

Try / catch

try:
    for chunk in resp: process(chunk)
except TypeError as e:
    if "sync iteration" in str(e):
        asyncio.run(consume_async(resp))

Prevention

When it happens

Trigger: Iterating the returned streaming response synchronously when the underlying call was async — e.g. holding a response produced via the async file-content path and then calling iter()/next() or a `for` loop over it inside sync code.

Common situations: Mixing sync and async lifecycles (async with ... : resp = await acreate_file_content... then sync iteration); passing an httpx.AsyncClient-produced byte stream into the sync wrapper; test helpers that iterate without regard to flavor.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/39d6ffa49d431db8. Report an issue: GitHub.