Bug Report: DeepSeek V4 Flash Free via OpenCode Zen provider 会话卡死
OpenClaw 版本: 2026.5.22 (a374c3a)
运行环境: WSL2 (Ubuntu 24.04), Node.js v22.22.2
OpenCode API: https://opencode.ai/zen/v1
问题描述
通过 OpenCode Zen provider 使用 deepseek-v4-flash-free 模型时,首次对话正常,但只要模型发起 tool call 并需要第二次 API 调用时,OpenClaw 就会收到 400 错误,会话卡死在 "in progress" 状态,仅能通过重置会话恢复。
错误信息:
400 Error from provider (DeepSeek):
The `reasoning_content` in the thinking mode must be passed back to the API.
复现步骤
- 配置 OpenCode Zen provider,使用
deepseek-v4-flash-free 模型(reasoning: true)
- 发送一条需要 tool call 的消息(如 "今天天气怎么样")
- 模型第一次响应正常,返回 thinking + tool_calls
- tool 执行完毕后,OpenClaw 将 tool 结果发回给 API 发起第二次调用
- 第二次 API 调用返回 400 错误 → 会话卡死
会话日志中的完整错误
{
"type": "message",
"message": {
"role": "assistant",
"content": [],
"api": "openai-completions",
"provider": "opencode",
"model": "deepseek-v4-flash-free",
"stopReason": "error",
"errorMessage": "400 Error from provider (DeepSeek): The `reasoning_content` in the thinking mode must be passed back to the API.",
"errorCode": "invalid_request_error",
"errorType": "invalid_request_error"
}
}
根因分析
涉及 4 处代码问题,它们叠加导致了这个 bug:
问题一:isDeepSeek 检测不匹配 OpenCode 路由
文件: dist/provider-model-compat-CmPOKTzc.js (第 12 行)
const isDeepSeek = endpointClass === "deepseek-native" ||
isDefaultRoute && isDefaultRouteProvider(input.provider, "deepseek");
通过 opencode.ai 访问 DeepSeek 时,端点类型为 "opencode-native"(不是 "deepseek-native"),provider 名为 "opencode"(不是 "deepseek")。因此 isDeepSeek = false,导致:
- 第 25 行:
thinkingFormat 被设为 "openai" 而非 "deepseek"
- 第 28 行:
requiresReasoningContentOnAssistantMessages 被设为 false
问题二:reasoning_content 被错误地剥离
文件: dist/openai-transport-stream-Pgx5hpN7.js (第 3650-3680 行)
shouldPreserveReasoningContentReplay() 因上述原因返回 false,导致 sanitizeCompletionsReasoningReplayFields() 在构建后续 API 请求时删除了 assistant 消息中的 reasoning_content 字段。但 DeepSeek V4 的 API 在 thinking mode 下要求保留 reasoning_content。
问题三:deepseek-v4-flash-free 不在回放白名单中
文件: dist/openai-transport-stream-Pgx5hpN7.js (第 3625-3642 行)
const REASONING_CONTENT_REPLAY_MODEL_IDS = new Set([
"deepseek-v4-flash", // 不含 -free 后缀
"deepseek-v4-pro",
"kimi-for-coding",
"kimi-k2.5",
"kimi-k2.6",
"kimi-k2-thinking",
"kimi-k2-thinking-turbo",
"mimo-v2-pro",
"mimo-v2-omni",
"mimo-v2.5",
"mimo-v2.5-pro",
"mimo-v2.6-pro"
]);
API 返回的模型 ID 是 "deepseek-v4-flash-free"。getReasoningContentReplayModelIdCandidates() 提取到 "deepseek-v4-flash-free",但白名单中只有 "deepseek-v4-flash"(不含 -free 后缀),导致匹配失败。
问题四:OpenCode Zen 缺少 DeepSeek V4 的 stream wrapper
文件: dist/extensions/opencode/index.js
OpenCode Zen 注册 provider 时没有设置 wrapStreamFn:
api.registerProvider({
id: "opencode",
...
// 缺少 wrapStreamFn
});
而 OpenCode Go(dist/extensions/opencode-go/index.js,第 90 行)有正确实现:
wrapStreamFn: (ctx) => createOpencodeGoWrapper(ctx.streamFn, ctx.thinkingLevel),
即使加上 wrapper,createOpencodeGoDeepSeekV4Wrapper(dist/stream-DcrKLb1r.js,第 6-7 行)也只匹配不含后缀的 ID:
function isOpencodeGoDeepSeekV4ModelId(modelId) {
return modelId === "deepseek-v4-flash" || modelId === "deepseek-v4-pro";
}
建议修复方案
方案 A(最小改动,推荐)
将 deepseek-v4-flash-free(及所有 *-free 后缀变体)加入 REASONING_CONTENT_REPLAY_MODEL_IDS 白名单,使 shouldPreserveReasoningContentReplay 返回 true,从而避免 reasoning_content 被剥离。
修改位置:dist/openai-transport-stream-Pgx5hpN7.js 第 3625 行
const REASONING_CONTENT_REPLAY_MODEL_IDS = new Set([
"deepseek-v4-flash",
+ "deepseek-v4-flash-free",
"deepseek-v4-pro",
同时建议将匹配方式改为 startsWith 或包含检测,而非严格等于,以适配各种后缀变体。
方案 B(彻底修复)
- 将
opencode-native 端点纳入 isDeepSeek 检测,或提供通用机制让代理类端点正确识别下游模型类型
- 为 OpenCode Zen provider 添加
wrapStreamFn(与 OpenCode Go 一致)
- 使
isOpencodeGoDeepSeekV4ModelId 支持后缀变体
用户侧临时绕过
将默认模型切换为 mimo-v2.5-free(该模型不触发 reasoning_content 回放要求)可正常工作。
补充信息
openclaw.json 中 OpenCode provider 配置:
{
"opencode": {
"baseUrl": "https://opencode.ai/zen/v1",
"api": "openai-completions",
"apiKey": "***",
"models": [
{ "id": "deepseek-v4-flash-free", "reasoning": true, ... }
]
}
}
- 模型 ID 在首次 API 响应中返回为
"deepseek-v4-flash"(OpenCode 映射去掉了 -free 后缀),但用户配置中为 "deepseek-v4-flash-free"
- 首次 API 调用(无历史 assistant 消息)正常,第二次调用(有
reasoning_content 的 assistant 消息)才出错
- 可通过
~/.openclaw/agents/main/sessions/ 下的 session JSONL 文件复现完整错误轨迹
Bug Report: DeepSeek V4 Flash Free via OpenCode Zen provider 会话卡死
OpenClaw 版本: 2026.5.22 (a374c3a)
运行环境: WSL2 (Ubuntu 24.04), Node.js v22.22.2
OpenCode API: https://opencode.ai/zen/v1
问题描述
通过 OpenCode Zen provider 使用
deepseek-v4-flash-free模型时,首次对话正常,但只要模型发起 tool call 并需要第二次 API 调用时,OpenClaw 就会收到 400 错误,会话卡死在 "in progress" 状态,仅能通过重置会话恢复。错误信息:
复现步骤
deepseek-v4-flash-free模型(reasoning: true)会话日志中的完整错误
根因分析
涉及 4 处代码问题,它们叠加导致了这个 bug:
问题一:
isDeepSeek检测不匹配 OpenCode 路由文件:
dist/provider-model-compat-CmPOKTzc.js(第 12 行)通过
opencode.ai访问 DeepSeek 时,端点类型为"opencode-native"(不是"deepseek-native"),provider 名为"opencode"(不是"deepseek")。因此isDeepSeek = false,导致:thinkingFormat被设为"openai"而非"deepseek"requiresReasoningContentOnAssistantMessages被设为false问题二:
reasoning_content被错误地剥离文件:
dist/openai-transport-stream-Pgx5hpN7.js(第 3650-3680 行)shouldPreserveReasoningContentReplay()因上述原因返回false,导致sanitizeCompletionsReasoningReplayFields()在构建后续 API 请求时删除了 assistant 消息中的reasoning_content字段。但 DeepSeek V4 的 API 在 thinking mode 下要求保留reasoning_content。问题三:
deepseek-v4-flash-free不在回放白名单中文件:
dist/openai-transport-stream-Pgx5hpN7.js(第 3625-3642 行)API 返回的模型 ID 是
"deepseek-v4-flash-free"。getReasoningContentReplayModelIdCandidates()提取到"deepseek-v4-flash-free",但白名单中只有"deepseek-v4-flash"(不含-free后缀),导致匹配失败。问题四:OpenCode Zen 缺少 DeepSeek V4 的 stream wrapper
文件:
dist/extensions/opencode/index.jsOpenCode Zen 注册 provider 时没有设置
wrapStreamFn:而 OpenCode Go(
dist/extensions/opencode-go/index.js,第 90 行)有正确实现:即使加上 wrapper,
createOpencodeGoDeepSeekV4Wrapper(dist/stream-DcrKLb1r.js,第 6-7 行)也只匹配不含后缀的 ID:建议修复方案
方案 A(最小改动,推荐)
将
deepseek-v4-flash-free(及所有*-free后缀变体)加入REASONING_CONTENT_REPLAY_MODEL_IDS白名单,使shouldPreserveReasoningContentReplay返回true,从而避免reasoning_content被剥离。修改位置:
dist/openai-transport-stream-Pgx5hpN7.js第 3625 行const REASONING_CONTENT_REPLAY_MODEL_IDS = new Set([ "deepseek-v4-flash", + "deepseek-v4-flash-free", "deepseek-v4-pro",同时建议将匹配方式改为
startsWith或包含检测,而非严格等于,以适配各种后缀变体。方案 B(彻底修复)
opencode-native端点纳入isDeepSeek检测,或提供通用机制让代理类端点正确识别下游模型类型wrapStreamFn(与 OpenCode Go 一致)isOpencodeGoDeepSeekV4ModelId支持后缀变体用户侧临时绕过
将默认模型切换为
mimo-v2.5-free(该模型不触发reasoning_content回放要求)可正常工作。补充信息
openclaw.json中 OpenCode provider 配置:{ "opencode": { "baseUrl": "https://opencode.ai/zen/v1", "api": "openai-completions", "apiKey": "***", "models": [ { "id": "deepseek-v4-flash-free", "reasoning": true, ... } ] } }"deepseek-v4-flash"(OpenCode 映射去掉了-free后缀),但用户配置中为"deepseek-v4-flash-free"reasoning_content的 assistant 消息)才出错~/.openclaw/agents/main/sessions/下的 session JSONL 文件复现完整错误轨迹