From 8992d600a7661be7c1c5a6b46ff17c8a959f4d60 Mon Sep 17 00:00:00 2001 From: Dan Aronson Date: Wed, 11 Feb 2026 11:53:28 +0200 Subject: [PATCH] Fix aiohttp ignore_localhost consuming response body When ignore_localhost=True and a request targets localhost, record_responses() would call response.read() to capture the body before cassette.append() checked filter_request(). For ignored hosts the response was never saved, but the body stream was already consumed, causing IncompleteReadError for callers that subsequently tried to read the response (e.g. aiobotocore). Skip record_responses() entirely when filter_request() indicates the request should be ignored. Co-Authored-By: Claude Opus 4.6 --- docs/changelog.rst | 3 +++ tests/integration/test_aiohttp.py | 18 ++++++++++++++++++ vcr/stubs/aiohttp_stubs.py | 3 ++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index e2a48831..ee156705 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,6 +7,9 @@ For a full list of triaged issues, bugs and PRs and what release they are target All help in providing PRs to close out bug issues is appreciated. Even if that is providing a repo that fully replicates issues. We have very generous contributors that have added these to bug issues which meant another contributor picked up the bug and closed it out. +- 8.1.2 + - Fix aiohttp ``ignore_localhost`` consuming response body, causing ``IncompleteReadError`` - thanks @AronsonDan + - 8.1.1 - Fix sync requests in async contexts for HTTPX (#965) - thanks @seowalex - CI: bump peter-evans/create-pull-request from 7 to 8 (#969) diff --git a/tests/integration/test_aiohttp.py b/tests/integration/test_aiohttp.py index bcef875e..1b7fd557 100644 --- a/tests/integration/test_aiohttp.py +++ b/tests/integration/test_aiohttp.py @@ -500,3 +500,21 @@ def test_use_cassette_with_io(tmpdir, caplog, httpbin): with vcr.use_cassette(str(tmpdir.join("post.yaml"))): _, response_json = request("POST", url, output="json", data=data) assert response_json["data"] == "hello" + + +@pytest.mark.online +def test_ignore_localhost_does_not_consume_response_body(tmpdir, httpbin): + """When ignore_localhost=True, VCR should not consume the response body. + + Previously, record_responses() would call response.read() to capture the + body before cassette.append() checked filter_request(). For ignored hosts, + the response was never saved but the body was already consumed, causing + IncompleteReadError for callers that later tried to read the response. + """ + url = f"http://localhost:{httpbin.port}/get" + + with vcr.use_cassette(str(tmpdir.join("ignore.yaml")), ignore_localhost=True) as cass: + response, response_text = get(url, output="text") + assert response.status == 200 + assert len(response_text) > 0 + assert len(cass) == 0 diff --git a/vcr/stubs/aiohttp_stubs.py b/vcr/stubs/aiohttp_stubs.py index 55f9060e..2a39efeb 100644 --- a/vcr/stubs/aiohttp_stubs.py +++ b/vcr/stubs/aiohttp_stubs.py @@ -278,7 +278,8 @@ async def new_request(self, method, url, **kwargs): log.info("%s not in cassette, sending to real server", vcr_request) response = await real_request(self, method, url, **kwargs) - await record_responses(cassette, vcr_request, response) + if cassette.filter_request(vcr_request): + await record_responses(cassette, vcr_request, response) return response return new_request