{"id":"d943e57a5f70c180","repo":"encode/httpx","slug":"elapsed-may-only-be-accessed-after-the-response","errorCode":null,"errorMessage":"'.elapsed' may only be accessed after the response has been read or closed.","messagePattern":"'\\.elapsed' may only be accessed after the response has been read or closed\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"httpx/_models.py","lineNumber":585,"sourceCode":"            self.stream = stream\n\n        self._num_bytes_downloaded = 0\n\n    def _prepare(self, default_headers: dict[str, str]) -> None:\n        for key, value in default_headers.items():\n            # Ignore Transfer-Encoding if the Content-Length has been set explicitly.\n            if key.lower() == \"transfer-encoding\" and \"content-length\" in self.headers:\n                continue\n            self.headers.setdefault(key, value)\n\n    @property\n    def elapsed(self) -> datetime.timedelta:\n        \"\"\"\n        Returns the time taken for the complete request/response\n        cycle to complete.\n        \"\"\"\n        if not hasattr(self, \"_elapsed\"):\n            raise RuntimeError(\n                \"'.elapsed' may only be accessed after the response \"\n                \"has been read or closed.\"\n            )\n        return self._elapsed\n\n    @elapsed.setter\n    def elapsed(self, elapsed: datetime.timedelta) -> None:\n        self._elapsed = elapsed\n\n    @property\n    def request(self) -> Request:\n        \"\"\"\n        Returns the request instance associated to the current response.\n        \"\"\"\n        if self._request is None:\n            raise RuntimeError(\n                \"The request instance has not been set on this response.\"\n            )","sourceCodeStart":567,"sourceCodeEnd":603,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_models.py#L567-L603","documentation":"RuntimeError raised by the Response.elapsed getter when _elapsed has not been set. httpx sets _elapsed inside the client's request machinery only after the response has been fully read or closed. Accessing response.elapsed on a Response you constructed yourself (Response(200, ...)) or before the request cycle completes therefore fails.","triggerScenarios":"Accessing resp.elapsed on a Response constructed via httpx.Response(...) directly; accessing .elapsed inside a stream iteration before the body has been fully read/closed; accessing .elapsed on a response obtained from a mock/replay transport that does not set elapsed; reading .elapsed before aread()/close() in async code.","commonSituations":"Unit tests building Response objects manually; mock transports that omit elapsed; inspecting resp.elapsed inside an `async for chunk in resp.aiter_bytes()` loop (elapsed set only after the loop ends and close() runs); partial reads where the user did not consume the body.","solutions":["Fully read or close the response first: `resp.read()` / `await resp.aread()` / `resp.close()`, then access resp.elapsed.","For Responses you construct yourself (mocks/tests), set elapsed explicitly: resp.elapsed = datetime.timedelta(seconds=0.1).","Guard access: `elapsed = getattr(resp, '_elapsed', None)`.","When streaming, only read .elapsed after the iteration completes (after close() has run)."],"exampleFix":"// before\nresp = httpx.Response(200, content=b'hi')\nprint(resp.elapsed)  # RuntimeError\n// after\nresp = httpx.Response(200, content=b'hi')\nresp.elapsed = datetime.timedelta(seconds=0.05)\nprint(resp.elapsed)\n# for real responses:\nresp = client.get(url)\nresp.read()  # ensures elapsed is set\nprint(resp.elapsed)","handlingStrategy":"validation","validationCode":"# Ensure the response has been read/closed before timing access\nif not hasattr(resp, '_elapsed'):\n    resp.read()  # or resp.close(); for async use await resp.aread()\nelapsed = resp.elapsed","typeGuard":"import datetime\n\ndef response_elapsed_ready(resp: 'httpx.Response') -> bool:\n    return isinstance(getattr(resp, '_elapsed', None), datetime.timedelta)","tryCatchPattern":"try:\n    elapsed = resp.elapsed\nexcept RuntimeError:\n    elapsed = None  # response not yet read/closed; or set a default","preventionTips":["Always read or close the response before accessing .elapsed.","For Responses you construct (tests/mocks), set elapsed explicitly.","Guard access with hasattr(resp, '_elapsed') or getattr.","In streaming code, read .elapsed only after the iteration completes."],"tags":["response","timing","api-misuse","mocking"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}