Skip to content

fix(deps): raise aiohttp floor to 3.14.2 to clear pooled-connection timeouts - #35337

Merged
yuneng-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_/aiohttp-regression-c3f059
Aug 1, 2026
Merged

fix(deps): raise aiohttp floor to 3.14.2 to clear pooled-connection timeouts#35337
yuneng-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_/aiohttp-regression-c3f059

Conversation

@yuneng-berri

Copy link
Copy Markdown
Collaborator

TLDR

Problem this solves:

  • uv.lock pinned aiohttp 3.14.1, which poisons pooled connections
  • Reused keep-alive connections fail instantly with bogus timeouts
  • Hits every provider at once, since they share one pool
  • Published images install from that lock, so all shipped it
  • Wheel metadata allowed aiohttp>=3.10, so pip could land there too

How it solves it:

  • Raise the runtime floor and uv constraint to aiohttp>=3.14.2
  • Relock; aiohttp moves 3.14.1 to 3.14.3, nothing else changes
  • Two tests fail if either the floor or the lock regresses

Relevant issues

Fixes #33820

Linear ticket

Resolves LIT-4726

Pre-Submission checklist

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Screenshots / Proof of Fix

Both runs below are at commit ffd6ac52c5, against the real Anthropic API. The only variable is the aiohttp version, because the aiohttp version is the entire change: "before" is the 3.14.1 that uv.lock pinned, "after" is the 3.14.3 this PR locks.

The probe opens one pooled connection, then issues real POST https://api.anthropic.com/v1/messages calls with a 4s idle gap against a 2s sock_read, so each call lands on a connection that has been sitting in the keepalive pool. aiohttp's own tracing hooks report whether each call got a fresh connection (NEW) or a pooled one (REUSE).

Before, on aiohttp 3.14.1:

  call 1: status=200 conn=NEW
  call 2: status=SocketTimeoutError: Timeout on reading data from socket conn=REUSE
  call 3: status=200 conn=NEW
  call 4: status=SocketTimeoutError: Timeout on reading data from socket conn=REUSE
  ...
  call 13: status=SocketTimeoutError: Timeout on reading data from socket conn=REUSE
  call 14: status=200 conn=NEW

aiohttp 3.14.1  idle=4.0s sock_read=2.0s
  new connections: 7   reuses: 7
  REUSE FAILURE RATE: 6/7

Every fresh connection succeeds and almost every pooled one fails, which is the reported symptom: the failure belongs to whichever request happens to pick up a connection an earlier, unrelated request left poisoned.

After, on aiohttp 3.14.3:

  call 1: status=200 conn=NEW
  call 2: status=200 conn=REUSE
  ...
  call 14: status=200 conn=REUSE

aiohttp 3.14.3  idle=4.0s sock_read=2.0s
  new connections: 1   reuses: 13
  REUSE FAILURE RATE: 0/13

One connection now serves all 14 calls, so keepalive reuse is intact rather than merely avoided.

The same before/after holds without a provider account, using the upstream reproducer shape against a local aiohttp server, where the timing signature is unambiguous:

### aiohttp 3.14.1
  round 1: FAILED  (0.3 ms) SocketTimeoutError: Timeout on reading data from socket
  round 3: FAILED  (0.2 ms) SocketTimeoutError: Timeout on reading data from socket
  round 5: FAILED  (0.1 ms) SocketTimeoutError: Timeout on reading data from socket
  aiohttp 3.14.1: 3/6 pooled reuses failed -> POISONED

### aiohttp 3.14.3
  aiohttp 3.14.3: 0/6 pooled reuses failed -> CLEAN

Those failures land in 0.1-0.3 ms against a 1s timeout, matching the time taken=0.001 seconds in the issue report; they are not real waits.

Also confirmed that a proxy on the new lock serves live traffic normally, via POST /v1/chat/completions through litellm/proxy/proxy_cli.py to anthropic/claude-haiku-4-5, 8/8 HTTP 200 with distinct real completions at 1.1-2.4s.

Type

🐛 Bug Fix

Changes

aiohttp 3.14.0 and 3.14.1 re-arm the sock_read timer on a keep-alive connection after it has already been returned to the idle pool. The stray timer stamps a SocketTimeoutError on the pooled connection without closing it, and aiohttp's pool acquisition never rechecks that flag, so it keeps handing the connection out. The next request to pick it up fails immediately on an error left behind by an earlier request. Because one pool is shared across providers, the failures surface simultaneously across Vertex AI, Bedrock, Anthropic and OpenAI-compatible deployments, which is what made this look provider-independent in the field.

Upstream fixed it in aio-libs/aiohttp#12954, released in aiohttp 3.14.2. That release did not exist when this was first triaged, which is why the earlier attempts had to work around it.

The exposure was through two separate declarations. uv.lock resolved 3.14.1, and the published images install with uv sync --frozen, so every image built from that lock shipped the regression. Separately the wheel's own metadata declared aiohttp>=3.10,<4.0, which left anyone installing via pip free to resolve into the same broken window regardless of the lock; a user reporting this on a version whose lock was clean is explained by exactly that. Both move to >=3.14.2.

Raising the floor rather than capping below 3.14 matters for CI hygiene: the existing 3.14.1 floor is what cleared the aiohttp advisories that osv-scanner checks, so moving up keeps that coverage and needs no new ignore entries, whereas capping below 3.14 would reintroduce nine of them.

litellm requires Python >=3.10 and aiohttp 3.14.2 requires >=3.10, so no supported interpreter loses support. Verified with uv pip install --resolution=lowest-direct . on 3.10 through 3.13, each resolving aiohttp to exactly 3.14.2. Two pre-existing failures surfaced during that check and are unrelated to this change, reproducing identically on the base commit: the lowest-direct resolve cannot build tiktoken==0.8.0 on Python 3.14, and a base-SDK install cannot import litellm because litellm/integrations/otel/model/config.py imports pydantic_settings, which ships only in the proxy extra. Both are worth their own PRs.

The relock is contained; comparing resolved name/version pairs across the whole lock, aiohttp 3.14.1 -> 3.14.3 is the only package that moved, with 436 packages before and after.

Two tests are added to tests/local_testing/test_basic_python_version.py, which already holds the packaging-metadata assertions and runs in CI. One asserts the declared runtime requirement admits neither 3.14.0 nor 3.14.1, covering the wheel that pip consumers resolve against. The other asserts the version uv.lock resolves is outside that range, covering the images. Both fail on the previous pins with the offending version named in the assertion message, and pass on these.

This supersedes #33822, which capped below 3.14 and was blocked on accepting the nine reintroduced advisories, and #33828, which added a HardenedTCPConnector to re-check the flag at acquisition. Both were correct responses to there being no fixed aiohttp release at the time. Now that 3.14.2 is out, the dependency bump makes the workaround code unnecessary, so I would suggest closing both rather than carrying a permanent connector subclass for a fixed upstream bug.

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

…imeouts

aiohttp 3.14.0 and 3.14.1 re-arm the sock_read timer on a keep-alive
connection after it has already been returned to the idle pool. The stray
timer stamps a SocketTimeoutError on the pooled connection without closing
it, so the pool keeps handing it out and the next request to pick it up
fails instantly on an error left behind by an earlier, unrelated request.
Because a single pool is shared across providers, the failures appear
simultaneously across Vertex AI, Bedrock, Anthropic and OpenAI-compatible
deployments as sub-millisecond "Connection timed out" errors.

uv.lock resolved aiohttp 3.14.1 and the published images install via
`uv sync --frozen`, so every image built from that lock shipped the
regression. The wheel's own metadata declared `aiohttp>=3.10,<4.0`, which
also left pip consumers free to resolve into the same broken window, so
both the runtime floor and the uv constraint move to >=3.14.2.

Upstream fixed this in aio-libs/aiohttp#12954, released in aiohttp 3.14.2;
the lock now resolves 3.14.3. Raising the floor rather than capping below
3.14 keeps the advisories that the existing 3.14.1 floor cleared, so no
osv-scanner ignores are needed. litellm requires Python >=3.10 and aiohttp
3.14.2 requires >=3.10, so no supported interpreter loses support.

Both new tests fail on the previous pins and pass on these.
@yuneng-berri
yuneng-berri requested a review from a team July 31, 2026 07:23
@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Raises the minimum supported aiohttp version to avoid pooled-connection timeout regressions.

  • Updates the runtime and uv constraints to require aiohttp 3.14.2 or newer.
  • Relocks aiohttp from 3.14.1 to 3.14.3.
  • Adds regression tests covering both package metadata and the locked version.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
pyproject.toml Raises both aiohttp dependency declarations to a fixed release floor.
tests/local_testing/test_basic_python_version.py Adds packaging and lockfile assertions excluding the affected aiohttp releases.
uv.lock Relocks aiohttp at 3.14.3 and updates its distribution artifacts.

Reviews (2): Last reviewed commit: "fix(deps): raise aiohttp floor to 3.14.2..." | Re-trigger Greptile

Comment thread pyproject.toml
"click>=8.0.0,<9.0",
"jinja2>=3.1.6,<4.0",
"aiohttp>=3.10,<4.0",
"aiohttp>=3.14.2,<4.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Conflicting aiohttp workflow requirements

When the scheduled price and context-window update runs with --with 'aiohttp==3.13.3', the new aiohttp>=3.14.2 project floor makes the requirements unsatisfiable, causing dependency resolution to fail before the update script runs.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed-hq

codspeed-hq Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_/aiohttp-regression-c3f059 (ffd6ac5) with litellm_internal_staging (05c9815)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (3c2264c) during the generation of this report, so 05c9815 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@yuneng-berri

Copy link
Copy Markdown
Collaborator Author

@greptile uv --with installs an overlay layer, so the pin shadows the project floor instead of conflicting. Ran the exact command; aiohttp resolves 3.13.3, workflow unaffected.

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

You're correct. uv --with creates an ephemeral overlay environment that shadows the project's declared constraints rather than conflicting with them, so the >=3.14.2 floor in pyproject.toml doesn't affect that workflow. Thanks for verifying — that's a valid point and I withdraw that concern.

@yuneng-berri

Copy link
Copy Markdown
Collaborator Author

@greptile review again please

@yuneng-berri
yuneng-berri merged commit eb4f33e into litellm_internal_staging Aug 1, 2026
79 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_/aiohttp-regression-c3f059 branch August 1, 2026 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: aiohttp 3.14.x connection-pool poisoning causes cross-provider "Connection timed out" failures (litellm pins aiohttp>=3.14.1, v1.91.0+)

3 participants