![[Bug]: github_copilot provider: 'no choices' error for claude-opus-4.8 despite #29392 fix](https://www.chat-gpts.plus/wp-content/uploads/2026/06/30927-f9610a47.jpg)
[Bug]: github_copilot provider: ‘no choices’ error for claude-opus-4.8 despite #29392 fix
快速结论:该报错发生在 LiteLLM 代理使用 github_copilot/claude-opus-4.8 模型时,convert_dict_to_response.py 检测到 choices: [] 空列表(而非缺失 choices 键)并抛出 no 'choices' 错误。尽管 PR #29392 已修复了 choices 键完全缺失的场景,但此 bug 属于不同的代码路径——父类 OpenAIConfig 可能重新读取了未经修补的原始响应字典,导致 transform_response 的修补未生效。优先排查 transform_response 修补后的 httpx.Response 是否被下游正确使用。
问题场景
用户通过 LiteLLM 代理向 GitHub Copilot 的 claude-opus-4.8 模型发送请求(Anthropic 格式 API 调用)。尽管已部署包含 PR #29392 修复的 LiteLLM 镜像,在使用 curl 或 Claude Code 客户端向代理发送 /v1/messages 请求时,代理返回 500 Internal Server Error,错误信息为 no 'choices'。
报错原文
litellm.APIError: LiteLLM: provider returned a response with no 'choices'.
Raw keys: ['id', 'choices', 'created', 'model', 'object', 'service_tier', 'system_fingerprint', 'usage', 'copilot_usage']
注意:choices 存在于原始键中,但其值为空列表 [],而非缺失。错误在 convert_dict_to_response.py:571 中抛出,该行位于 transform_response 执行之后。
原因分析
该 bug 与 PR #29392 修复的场景不同。PR #29392 在 GithubCopilotConfig.transform_response() 中处理了 Copilot 返回 Anthropic 原生格式(不带 choices 键)的情况,并通过修补创建了新的 httpx.Response。然而当前 bug 的触发条件是响应中 choices 键存在但值为空列表 []。虽然 transform_response 方法中的 if not response_json.get("choices") 守卫理论上应捕获此情况(空列表在 Python 中为假值),但修补后的 httpx.Response 可能未被下游使用——可能原因是父类 OpenAIConfig 在后续处理中重新读取了原始的未修补响应字典,而非使用修补后的响应对象。此外,也存在以下可能原因:
- provider 特定响应解析器读取了错误的字段。
- 回退/错误路径检查了已被转换的对象。
- 流式与非流式响应的形状处理不一致。
- 响应包含 provider 特定包装,
choices位于比预期更深一层的嵌套中。
环境排查
- LiteLLM 镜像版本:
docker.litellm.ai/berriai/litellm-database:main-stable(构建于 2026-06-20,应包含 #29392 修复,commit65be6ff)。 - GitHub Enterprise (GHE) Copilot 端点。
- 确认是否使用 Claude Code 客户端或
curl发送 Anthropic 格式请求(/v1/messages 端点)。
解决步骤
- 确认响应形状:在
transform_response修补前后添加日志,记录原始上游响应体与修补后的httpx.Response内容,验证choices: []是否被正确修补为有效值。 - 检查修补响应流向下游的路径:跟踪
transform_response返回的修补httpx.Response是否在OpenAIConfig后续逻辑中被覆盖或忽略——特别关注convert_dict_to_response.py:571调用栈之前的llms/openai/openai.py中的acompletion方法。 - 验证不同代码路径:区分流式与非流式请求的处理分支,确认问题是否仅出现在非流式请求中(因为
transform_response可能对两者处理不同)。 - 建议调试工具:使用请求/响应记录工具(如 ccglass)记录原始请求体、上游响应体/SSE 流、使用量、延迟及模型/provider 元数据,以对比线上数据与 LiteLLM 转换后的
ModelResponse对象。 - 确认是否是响应形状归一化 bug:若修补响应未被下游使用,则需要在
OpenAIConfig或其基类中修改逻辑,确保使用修补后的响应对象,而非直接重新解析原始响应字典。 - 可以考虑的临时措施(推断,可优先尝试):在
transform_response修补后,直接修改response_object(即response_json本身)而非仅创建新的httpx.Response,确保在任何代码路径下choices都非空。具体需要检查transform_response的当前实现是否仅修补了httpx.Response而遗漏了response_json的原地修改。
验证方法
重复调用以下 curl 命令(设备流认证完成后),确认返回 HTTP 200 及有效 completion 而不是 500 错误:
curl -s http://localhost:4000/v1/messages \
-H "Authorization: Bearer $KEY" \
-H "content-type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"copilot-opus","max_tokens":1,"messages":[{"role":"user","content":"Hi"}]}'
同时确认日志中不再出现 no 'choices' 错误。
参考来源
相关修复 PR:BerriAI/litellm #29392



