{"id":"7f613e413ffa741d","repo":"encode/httpx","slug":"attempted-to-call-an-async-close-on-a-sync-stream","errorCode":null,"errorMessage":"Attempted to call an async close on a sync stream.","messagePattern":"Attempted to call an async close on a sync stream\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"httpx/_models.py","lineNumber":1071,"sourceCode":"\n        with request_context(request=self._request):\n            async 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        await self.aclose()\n\n    async def aclose(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, AsyncByteStream):\n            raise RuntimeError(\"Attempted to call an async close on a sync stream.\")\n\n        if not self.is_closed:\n            self.is_closed = True\n            with request_context(request=self._request):\n                await self.stream.aclose()\n\n\nclass Cookies(typing.MutableMapping[str, str]):\n    \"\"\"\n    HTTP Cookies, as a mutable mapping.\n    \"\"\"\n\n    def __init__(self, cookies: CookieTypes | None = None) -> None:\n        if cookies is None or isinstance(cookies, dict):\n            self.jar = CookieJar()\n            if isinstance(cookies, dict):\n                for key, value in cookies.items():\n                    self.set(key, value)","sourceCodeStart":1053,"sourceCodeEnd":1089,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_models.py#L1053-L1089","documentation":"Raised as `RuntimeError` by async `aclose()` when `self.stream` is not an `AsyncByteStream`. You awaited the async close on a Response whose underlying stream is sync (came from `httpx.Client`).","triggerScenarios":"Calling `await response.aclose()` on a Response produced by sync `httpx.Client`.","commonSituations":"Generic async cleanup helpers that always `await resp.aclose()`; mixing a sync client into an async service; partial migration leaving sync clients behind.","solutions":["Use `response.close()` for sync-client responses.","Keep cleanup logic paired with the matching client type.","Switch to `httpx.AsyncClient` if the surrounding code is async.","Branch the cleanup: `if isinstance(resp.stream, httpx._transports.default.AsyncByteStream): await resp.aclose() else: resp.close()`."],"exampleFix":"// before\nr = sync_client.get(url)\nawait r.aclose()  # RuntimeError\n\n// after\nr = sync_client.get(url)\nr.close()","handlingStrategy":"type-guard","validationCode":"from httpx._transports.default import SyncByteStream\n\ndef needs_close(resp: httpx.Response) -> bool:\n    return isinstance(resp.stream, SyncByteStream)","typeGuard":"import httpx\nfrom httpx._transports.default import SyncByteStream\n\ndef is_sync_stream(resp: httpx.Response) -> bool:\n    return isinstance(resp.stream, SyncByteStream)","tryCatchPattern":"try:\n    await response.aclose()\nexcept RuntimeError:\n    response.close()","preventionTips":["close() sync responses; aclose() async responses.","Keep cleanup paired with the originating client type.","Migrate to AsyncClient if the surrounding code is async."],"tags":["sync-async","streaming","asyncio","close","runtime-error"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}