fix(deps): pin aiohttp below 3.14 to avoid pooled-connection timeout regression - #33822
fix(deps): pin aiohttp below 3.14 to avoid pooled-connection timeout regression#33822devin-ai-integration[bot] wants to merge 3 commits into
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
Greptile SummaryThis PR pins
Confidence Score: 4/5Safe to merge; the change is a targeted dependency pin with well-documented rationale, a matching lockfile update, and a regression guard test. The core change (constraint pin + lockfile) is straightforward and low-risk. The new test correctly enforces the constraint but will throw an unhandled FileNotFoundError rather than skipping if pyproject.toml is absent from the expected relative path, which could cause confusing failures in non-checkout environments. tests/local_testing/test_basic_python_version.py — the path-resolution logic for pyproject.toml deserves a second look.
|
| Filename | Overview |
|---|---|
| pyproject.toml | Constraint-dependencies pin for aiohttp downgraded from >=3.14.1,<4.0 to >=3.13.5,<3.14 to avoid the keep-alive pool poisoning regression in 3.14.x; well-justified and matches the intent of the PR. |
| osv-scanner.toml | Adds two CVE ignore entries (GHSA-hg6j-4rv6-33pg, GHSA-jg22-mg44-37j8) with expiry 2026-10-15 and detailed justifications; tradeoff is clearly documented. |
| tests/local_testing/test_basic_python_version.py | Adds test_aiohttp_constraint_excludes_314 to enforce the version pin; pyproject.toml path resolution will raise FileNotFoundError (rather than skipping) in non-editable-install environments. |
| uv.lock | Lockfile re-solved with aiohttp 3.14.1 -> 3.13.5; exclude-newer timestamp also bumped, which is expected when relocking. |
Reviews (1): Last reviewed commit: "fix(deps): pin aiohttp below 3.14 to avo..." | Re-trigger Greptile
| pyproject_path = pathlib.Path(litellm.__file__).parent.parent / "pyproject.toml" | ||
| with open(pyproject_path, "rb") as f: | ||
| constraints = tomli.load(f)["tool"]["uv"]["constraint-dependencies"] |
There was a problem hiding this comment.
If
pyproject.toml isn't found at the computed path (e.g. when running against a non-editable wheel install), open() raises FileNotFoundError and the test fails with an unhandled exception instead of skipping gracefully. Wrapping the file open in a similar guard makes the test self-contained regardless of install mode.
| pyproject_path = pathlib.Path(litellm.__file__).parent.parent / "pyproject.toml" | |
| with open(pyproject_path, "rb") as f: | |
| constraints = tomli.load(f)["tool"]["uv"]["constraint-dependencies"] | |
| pyproject_path = pathlib.Path(litellm.__file__).parent.parent / "pyproject.toml" | |
| if not pyproject_path.exists(): | |
| pytest.skip(f"pyproject.toml not found at {pyproject_path} - skipping dependency check") | |
| with open(pyproject_path, "rb") as f: | |
| constraints = tomli.load(f)["tool"]["uv"]["constraint-dependencies"] |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Relevant issues
Fixes #33820
Linear ticket
Pre-Submission checklist
Screenshots / Proof of Fix
The regression lives entirely in aiohttp, so the proof runs the self-contained repro from aio-libs/aiohttp#12953 against the two aiohttp versions this PR moves between. No mocks; a real aiohttp server and a real keep-alive pooled connection. Captured at commit
d21795864c9f6beb5b8e12419d7743627bb8626fBefore (what v1.91.0+ resolves today,
aiohttp==3.14.1):After (what this PR resolves,
aiohttp==3.13.5):The repro (from the upstream issue) opens a local aiohttp server, makes one request whose buffered body is read, checks the pooled connection, idles past
sock_readbut underkeepalive_timeout, then makes a second request that reuses the pooled connection:The lockfile confirms the resolution moves with the constraint:
Type
🐛 Bug Fix
Changes
Since v1.91.0 the uv
constraint-dependenciespin raises the aiohttp floor to>=3.14.1,<4.0, and 3.14.x carries a keep-alive pool poisoning regression (aio-libs/aiohttp#12953). Reading the already-buffered body of a completed response callsresume_reading(), which in 3.14 lost its_reading_pausedguard and re-arms thesock_readtimer on a connection that has already been returned to the keep-alive pool. Whenkeepalive_timeout > sock_read, that timer later stamps aSocketTimeoutErroron the idle pooledResponseHandlerwithout closing the transport, so the next request to grab it fails instantly withlitellm.Timeout: Connection timed out ... time taken=0.001 seconds. Because it is the shared pool, this shows up as sporadic sub-millisecond timeouts across every provider at once (Vertex, Bedrock, Anthropic, OpenAI-compatible)The upstream fix (aio-libs/aiohttp#12954) merged on 2026-06-18 but has not shipped in any release; the latest aiohttp is still 3.14.1 (uploaded 2026-06-07), so no aiohttp release currently has both the two medium CVEs fixed and the regression fixed. This PR caps the constraint back to
>=3.13.5,<3.14, matching what v1.90.0 already shipped, and relocks (aiohttp 3.14.1 -> 3.13.5)Holding below 3.14 reintroduces the two medium advisories that the 3.14.1 bump cleared, so their osv-scanner ignore entries come back. Both barely apply to litellm: GHSA-hg6j-4rv6-33pg only leaks cookies set via the per-request
cookiesparameter across a cross-origin redirect, and GHSA-jg22-mg44-37j8 only bitesCookieJar.load()on attacker-controlled files; a sporadic cross-provider outage is the worse tradeoff. The ignore entries and the constraint should both be dropped once a 3.14.x release ships the #12954 fixtests/local_testing/test_basic_python_version.py::test_aiohttp_constraint_excludes_314parses the constraint and fails if the pin ever admits 3.14.0 or 3.14.1 again, so this cannot silently flip back the way it did between v1.89.3 and v1.91.0Final Attestation
Link to Devin session: https://app.devin.ai/sessions/97741dd6c63a4f7f9bef188126178284