{"id":"7154502aebe06f73","repo":"encode/httpx","slug":"attempted-to-call-a-sync-close-on-an-async-stream","errorCode":null,"errorMessage":"Attempted to call a sync close on an async stream.","messagePattern":"Attempted to call a sync close on an async stream\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"httpx/_models.py","lineNumber":967,"sourceCode":"\n        with request_context(request=self._request):\n            for raw_stream_bytes in self.stream:\n                self._num_bytes_downloaded += len(raw_stream_bytes)\n                for chunk in chunker.decode(raw_stream_bytes):\n                    yield chunk\n\n        for chunk in chunker.flush():\n            yield chunk\n\n        self.close()\n\n    def close(self) -> None:\n        \"\"\"\n        Close the response and release the connection.\n        Automatically called if the response body is read to completion.\n        \"\"\"\n        if not isinstance(self.stream, SyncByteStream):\n            raise RuntimeError(\"Attempted to call a sync close on an async stream.\")\n\n        if not self.is_closed:\n            self.is_closed = True\n            with request_context(request=self._request):\n                self.stream.close()\n\n    async def aread(self) -> bytes:\n        \"\"\"\n        Read and return the response content.\n        \"\"\"\n        if not hasattr(self, \"_content\"):\n            self._content = b\"\".join([part async for part in self.aiter_bytes()])\n        return self._content\n\n    async def aiter_bytes(\n        self, chunk_size: int | None = None\n    ) -> typing.AsyncIterator[bytes]:\n        \"\"\"","sourceCodeStart":949,"sourceCodeEnd":985,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_models.py#L949-L985","documentation":"Raised as `RuntimeError` by sync `close()` when `self.stream` is not a `SyncByteStream`. You called the sync close on a Response whose underlying stream is async (came from `AsyncClient`). Closing an async stream synchronously would not release the connection correctly, so it is rejected.","triggerScenarios":"Calling `response.close()` (sync) on a Response produced by `httpx.AsyncClient`.","commonSituations":"Same as error 48: mixing sync/async clients, partial async migration, shared helpers, or framework finalizers that call `.close()` regardless of the transport type.","solutions":["Use `await response.aclose()` for async responses.","Inside an async context manager, prefer `async with client.stream(...)` which acloses automatically.","Branch on transport type if a shared cleanup helper is unavoidable.","Keep sync/async response lifecycles fully separated."],"exampleFix":"// before\nr = await async_client.get(url)\nr.close()  # RuntimeError\n\n// after\nr = await async_client.get(url)\nawait r.aclose()","handlingStrategy":"type-guard","validationCode":"from httpx._transports.default import AsyncByteStream\n\ndef needs_aclose(resp: httpx.Response) -> bool:\n    return isinstance(resp.stream, AsyncByteStream)","typeGuard":"import httpx\nfrom httpx._transports.default import AsyncByteStream\n\ndef is_async_stream(resp: httpx.Response) -> bool:\n    return isinstance(resp.stream, AsyncByteStream)","tryCatchPattern":"try:\n    response.close()\nexcept RuntimeError:\n    await response.aclose()","preventionTips":["Always aclose() async responses; close() sync responses.","Prefer async with client.stream(...) which manages aclose automatically.","Never share cleanup helpers across sync/async code paths."],"tags":["sync-async","streaming","asyncio","close","runtime-error"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}