From a838be5e9d4cf1883cad109e75c9db87ed84b7c0 Mon Sep 17 00:00:00 2001 From: David <12414531+DavidBellamy@users.noreply.github.com> Date: Tue, 5 May 2026 09:14:01 -0700 Subject: [PATCH] Drop upstream Server header in session-server proxy response When the session server proxies a response back from smg's main router, it now strips the upstream Server header before constructing the FastAPI response. Without this, FastAPI/uvicorn adds its own Server header on top of the one already there, producing duplicate Server headers, which aiohttp's strict HTTP parser (used by litellm in harbor) rejects as malformed (BadHttpMessage 400). Harbor retries the request ~10 times then gives up, breaking agent rollouts whenever --use-session-server is enabled. Mirrors the existing strip-list pattern for content-length and transfer-encoding (same category of bug: a header the local HTTP stack regenerates). --- miles/rollout/session/session_server.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/miles/rollout/session/session_server.py b/miles/rollout/session/session_server.py index 0bf3b5a423..79020081c4 100644 --- a/miles/rollout/session/session_server.py +++ b/miles/rollout/session/session_server.py @@ -86,10 +86,13 @@ def build_proxy_response(self, result: dict) -> Response: # verbatim breaks uvicorn h11 with "Too much data for declared # Content-Length" whenever our re-serialization differs in even one # byte. Mirrors the strip already done on the request path in do_proxy. + # Also strip "server": uvicorn adds its own Server header; passing + # the upstream one through produces two Server headers, which strict + # HTTP parsers (aiohttp/llhttp via litellm) reject as malformed. headers = { k: v for k, v in result["headers"].items() - if k.lower() not in ("content-length", "transfer-encoding") + if k.lower() not in ("content-length", "transfer-encoding", "server") } content_type = headers.get("content-type", "") try: