Skip to content

Hotfix: force uvicorn h11 HTTP parser (Vercel 400 on POST /_dash-update-component) - #27

Merged
utkuvibing merged 1 commit into
mainfrom
hotfix/uvicorn-h11-parser
Aug 30, 2026
Merged

Hotfix: force uvicorn h11 HTTP parser (Vercel 400 on POST /_dash-update-component)#27
utkuvibing merged 1 commit into
mainfrom
hotfix/uvicorn-h11-parser

Conversation

@utkuvibing

Copy link
Copy Markdown
Owner

Problem

Every POST /_dash-update-component returns HTTP 400 on the Vercel container deployment. The Dash shell (GET /) serves, but dash.page_container never populates, and the browser shows secondary renderer errors (Failed to execute 'text' on 'Response': body stream already read) — Dash's error handler tripping over the same 400 body.

Root cause

dash_app/server.py called uvicorn.run(...) without an http argument, so uvicorn used http="auto" and silently selects the httptools parser whenever it is importable. httptools is not a declared dependency (requirements.txt has plain uvicorn), but it reaches container images transitively, and uvicorn>=0.30.0 resolves to current releases (0.46+) where the httptools path rejects valid proxy-generated request shapes with 400 Invalid HTTP request received. (uvicorn discussion #2934, PR #3097 — absolute-form request targets crash httptools's raw_path.decode("ascii") on path=None). h11 accepts the same bytes. Vercel's edge proxy is exactly the Node-based client class in those reports.

Local verification (before the fix)

Raw-socket replay of proxy-shaped requests against uvicorn 0.52.4 + httptools 0.8.0:

request shape http=auto (httptools) http=h11
origin-form POST 200 200
absolute-form target 200 200
absolute-form, empty path 400 Invalid HTTP request received. 200

The 400 is emitted by uvicorn's protocol layer (server log: WARNING: Invalid HTTP request received.) before any app code runs — matching the Vercel runtime log signature. After forcing http="h11", the same bytes reach the application layer (404/500 app-level answers, no parser-level 400), and the real combined server boots, serves /health 200 and the Dash shell.

Fix (smallest possible)

  • dash_app/server.py: uvicorn.run(..., http="h11") — h11 is uvicorn's own hard dependency and accepts the affected request shapes.
  • tests/test_deployment_contract.py: two focused contract tests —
    • AST pin that the entrypoint's uvicorn.run call passes http="h11";
    • runtime test that the pinned config resolves H11Protocol even when httptools is importable (simulated transitive install).

Scope

No changes to Dash page logic, Vercel routing, Dockerfile, or dependencies. backend/main.py (desktop-only backend entrypoint, not deployed) intentionally left as-is.

Validation

  • Full suite: 1174 passed, 15 skipped (Python 3.12 venv, pip install -e .[dev]).
  • ruff check . clean.

@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
material-scope Ready Ready Preview Aug 30, 2026 10:07pm

@utkuvibing
utkuvibing merged commit 372732c into main Aug 30, 2026
5 checks passed
@utkuvibing
utkuvibing deleted the hotfix/uvicorn-h11-parser branch August 30, 2026 22:35
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.

1 participant