类型 / Type
Bug(偶发 / intermittent)。思考模式(thought level = max)下,会话中途请求体从 Responses 形态静默回落到 Chat Completions 形态,且历史助手消息携带空字符串 reasoning_content,被网关以 400 拒收,整轮失败且不可重试。
环境 / Environment
- 系统:Windows 10 x64 10.0.19045;ZCode 桌面端 v3.11.2(
user-agent: ZCode/3.11.2,x-zcode-app-version: 3.11.2,x-title: Z Code@electron,production 通道)
- Provider:自定义 OpenAI 兼容(
providerKind: openai),baseURL https://api.bankofai.io/v1(one-api / new-api 类中转网关),声明 apiFormat: openai-responses,paths.openai = /responses
- 模型:
deepseek-v4.1-flash;思考档位 max(thinking: {type: enabled}、reasoningEffort: max)
- 会话形态:主链会话(
x-zcode-session-type: main),querySource: main_turn,工具调用密集的长任务(该轮请求体 50 条消息 / 25 次 tool-call)
现象 / Symptom
界面弹出错误:「Turn execution failed / provider_code=invalid_request_error / model=deepseek-v4.1-flash / status=400 / retryable=false」,完整报文为:
The `reasoning_text` in the thinking mode must be passed back to the API.
- TraceID:
00f83c03-746e-4528-94d0-ec966278e581
- 客户端 requestId:
cccf80b9-ea16-47a7-bc5b-471bd81818ca
- 网关侧 request-id(响应头
x-oneapi-request-id):20260916144416867069803c955d568e70zkCcj
- 日志定位:
~/.zcode/cli/log/zcode-2026-09-16.jsonl 事件 model.request.failed / model.network.failed(reason: invalid_request, retryable: false, canRetry: false, statusCode: 400)→ turn.failed
关键证据:请求体形态在会话中途被切换 / Evidence
对比留存于 ~/.zcode/cli/rollout/model-io-*.jsonl 的请求记录(本地时间 2026-09-16 22:43–22:44,UTC 14:43–14:44):
| 记录 |
时间 (UTC) |
body 形态 |
结果 |
| 前一条 |
14:43:44 |
input(Responses) |
200 成功 |
| 失败条 |
14:44:15 |
messages(Chat Completions) |
400 失败 |
| 后一条 |
14:45:29 |
input(Responses) |
200 成功 |
该会话全部请求记录中,唯一一次 Chat Completions 形态的请求就是失败这一条;同一时段另两个并行会话(共 90 条请求记录)全部为 Responses 形态、零失败。当天全部请求记录合计 139 条,Chat Completions 形态仅此 1 条。
失败请求体的关键特征(同时具备三处异常):
- 顶层字段从 Responses 的
input 变成 Chat Completions 的 messages;
- 全部 18 条历史助手消息都被写入
providerOptions.openaiCompatible.reasoning_content = ""(空字符串,一条不落);
- 请求体附带
bodySource: "ai_sdk_options"、experimental_include: {requestBody: true, responseBody: true}、stream: true —— 正常请求都不带这些字段。
"messages": [
...,
{ "role": "assistant",
"content": [ { "type": "text", "text": "..." },
{ "type": "tool-call", "toolCallId": "call_00_...", "toolName": "Read", "input": {...} } ],
"providerOptions": { "openaiCompatible": { "reasoning_content": "" } } } <-- 空串,18 条全部如此
]
对比正常(Responses)请求体:顶层为 input 数组,助手消息以 function_call / function_call_output 条目承载,完全不出现 reasoning 类字段(既不回传、也不送空串)。
排除项 / What it is NOT
- 不是模型或账号问题:同一 provider、同一模型、同一档位的前后请求均 200 成功。
- 不是网关故障:同一网关在失败前 30 秒与失败后 70 秒均正常返回;网关这次是正确地拒绝了不合规请求体(思考模式下必须回传思考内容)。
- 不是请求体超限(区别于已知的 >1MB 400 案例):本次请求体 50 条消息,远未触及上限。
- 不是 CASE-09 那类 SSE 瞬断:本次
reason 是 invalid_request、retryable: false,客户端没有自动重试(retryReason: network_error 但 canRetry: false),失败是确定性的,不是等自愈型。
期望行为 / Expected
- 会话中途不应无提示地在 Responses 与 Chat Completions 两种报文形态之间切换;若确有回退逻辑,应有显性日志/告警,并保证切换后报文仍然合规。
- 走 Chat Completions 形态且处于思考模式时,历史助手消息应真实回传其思考内容;若客户端拿不到思考内容,应整个省略
reasoning_content 字段,而不是送空字符串——空串在网关看来正是"该带的没带",必然 400。
- 若配置的
apiFormat(openai-responses)与实际发出的报文形态不一致,UI/日志应提示,而不是静默发出。
实际行为 / Actual
报文形态静默回退为 Chat Completions,历史助手消息的 reasoning_content 全为空串,网关 400 且标记不可重试 → 整个回合直接失败。用户只能看到一句报错,无从判断原因;重发一条消息(新回合)后即恢复正常(本机实测:14:45 续问,后续 20+ 次请求全部 200)。
重现步骤 / Repro(偶现,尽力描述)
- 配置自定义 OpenAI 兼容 provider(baseURL 指向网关,模型为 deepseek 类),思考档位置 max;
- 在一个工具调用密集的长会话中持续推进(本机触发时该回合已累计 25 次 tool-call、上下文 50 条消息);
- 观察
~/.zcode/cli/rollout/model-io-*.jsonl:偶发某一次请求的 body 从 input 变为 messages,助手消息带 reasoning_content: "";
- 该请求必然被网关以 400
reasoning_text ... must be passed back 拒绝,回合失败。
影响 / Impact
单次发生即导致整轮对话作废(不可重试),长任务需手动重发;由于报文形态切换无任何用户可见线索,极易误判为网关/账号故障。期望至少做到"回退后仍合规"或"送空串改为省略字段"。
相关 / Related
类型 / Type
Bug(偶发 / intermittent)。思考模式(thought level = max)下,会话中途请求体从 Responses 形态静默回落到 Chat Completions 形态,且历史助手消息携带空字符串
reasoning_content,被网关以 400 拒收,整轮失败且不可重试。环境 / Environment
user-agent: ZCode/3.11.2,x-zcode-app-version: 3.11.2,x-title: Z Code@electron,production 通道)providerKind: openai),baseURLhttps://api.bankofai.io/v1(one-api / new-api 类中转网关),声明apiFormat: openai-responses,paths.openai =/responsesdeepseek-v4.1-flash;思考档位 max(thinking: {type: enabled}、reasoningEffort: max)x-zcode-session-type: main),querySource: main_turn,工具调用密集的长任务(该轮请求体 50 条消息 / 25 次 tool-call)现象 / Symptom
界面弹出错误:「Turn execution failed / provider_code=invalid_request_error / model=deepseek-v4.1-flash / status=400 / retryable=false」,完整报文为:
00f83c03-746e-4528-94d0-ec966278e581cccf80b9-ea16-47a7-bc5b-471bd81818cax-oneapi-request-id):20260916144416867069803c955d568e70zkCcj~/.zcode/cli/log/zcode-2026-09-16.jsonl事件model.request.failed/model.network.failed(reason: invalid_request,retryable: false,canRetry: false,statusCode: 400)→turn.failed关键证据:请求体形态在会话中途被切换 / Evidence
对比留存于
~/.zcode/cli/rollout/model-io-*.jsonl的请求记录(本地时间 2026-09-16 22:43–22:44,UTC 14:43–14:44):input(Responses)messages(Chat Completions)input(Responses)该会话全部请求记录中,唯一一次 Chat Completions 形态的请求就是失败这一条;同一时段另两个并行会话(共 90 条请求记录)全部为 Responses 形态、零失败。当天全部请求记录合计 139 条,Chat Completions 形态仅此 1 条。
失败请求体的关键特征(同时具备三处异常):
input变成 Chat Completions 的messages;providerOptions.openaiCompatible.reasoning_content = ""(空字符串,一条不落);bodySource: "ai_sdk_options"、experimental_include: {requestBody: true, responseBody: true}、stream: true—— 正常请求都不带这些字段。对比正常(Responses)请求体:顶层为
input数组,助手消息以function_call/function_call_output条目承载,完全不出现 reasoning 类字段(既不回传、也不送空串)。排除项 / What it is NOT
reason是invalid_request、retryable: false,客户端没有自动重试(retryReason: network_error但canRetry: false),失败是确定性的,不是等自愈型。期望行为 / Expected
reasoning_content字段,而不是送空字符串——空串在网关看来正是"该带的没带",必然 400。apiFormat(openai-responses)与实际发出的报文形态不一致,UI/日志应提示,而不是静默发出。实际行为 / Actual
报文形态静默回退为 Chat Completions,历史助手消息的
reasoning_content全为空串,网关 400 且标记不可重试 → 整个回合直接失败。用户只能看到一句报错,无从判断原因;重发一条消息(新回合)后即恢复正常(本机实测:14:45 续问,后续 20+ 次请求全部 200)。重现步骤 / Repro(偶现,尽力描述)
~/.zcode/cli/rollout/model-io-*.jsonl:偶发某一次请求的 body 从input变为messages,助手消息带reasoning_content: "";reasoning_text ... must be passed back拒绝,回合失败。影响 / Impact
单次发生即导致整轮对话作废(不可重试),长任务需手动重发;由于报文形态切换无任何用户可见线索,极易误判为网关/账号故障。期望至少做到"回退后仍合规"或"送空串改为省略字段"。
相关 / Related