TinkerLab
DIAGNOSTIC TREE // SSE-HTTP200-STREAM-DROP (3 ROOT CAUSES)

LLM 流式接口在返回 HTTP 200 后中途报错或断流

排查“状态码明明是 200 OK,但生成到一半中断、报 JSON 解析错误或卡死”的运行期故障。

[核心实测结论 / TL;DR]本诊断树针对「LLM 流式接口在返回 HTTP 200 后中途报错或断流」提供 4 个交互节点与 3 套实测验证修复方案(涵盖:修复方案:在每个 SSE 数据帧中检测 error 字段,不能只检查 HTTP 状态码;修复方案:关闭反向代理缓冲并延长长上下文/推理模型读取超时;修复方案:解析结构化 JSON 前先校验 finish_reason 是否为 length 截断),配套完整实测指南《sse-http200-stream-errors》。
LLM 流式接口在返回 HTTP 200 后中途报错或断流
第 1 步:确认流式响应中断的具体表现

观察收到 HTTP 200 响应头之后的原始 SSE 数据流或日志,中断前发生了什么?

终端验证命令 (在对应环境执行)
curl -i -N http://127.0.0.1:11434/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"qwen2.5:7b","stream":true,"messages":[{"role":"user","content":"Hello"}]}'

选择你看到的实际输出或症状:
DIAGNOSTIC PROBE SEQUENCE // 完整分步探测命令与症状分支

「LLM 流式接口在返回 HTTP 200 后中途报错或断流」的全部 1 步终端探测命令与分支判据

无需逐层点击交互,以下列出本故障决策树每一层的宿主机只读探测命令、预期基线提示与输出流向。

第 1 步:确认流式响应中断的具体表现step-stream-symptom

观察收到 HTTP 200 响应头之后的原始 SSE 数据流或日志,中断前发生了什么?

curl -i -N http://127.0.0.1:11434/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"qwen2.5:7b","stream":true,"messages":[{"role":"user","content":"Hello"}]}'
输出症状分支:
  • • 流式数据帧中途收到了 {"error": ...} 或 type: "error" 事件→ sol-sse-midstream-error-handler
  • • 在长 Prompt 预填充或深度推理思考期间卡住,并在固定 60 秒断开→ sol-proxy-buffering-timeout
  • • 正常收到 [DONE] 结束符,但下游解析生成的 JSON 时报语法错误→ sol-finish-reason-length
ALL VERIFIED FIXES IN THIS TREE // 本树涵盖的全部根因与修复方案一览

「LLM 流式接口在返回 HTTP 200 后中途报错或断流」包含的 3 类根因、修复代码与验收命令

修复方案:在每个 SSE 数据帧中检测 error 字段,不能只检查 HTTP 状态码

sol-sse-midstream-error-handler

流式接口一旦发送了 HTTP 200 响应头,后续生成过程中若发生后端过载、显存不足或上下文超限,服务端已无法修改 HTTP 状态码,只能往 SSE 流内写入 error 事件。

  1. 在消费每个 `data:` JSON 帧时,先判断是否存在 `event.error` 或 `event.type === "error"`。
  2. 捕获到流内错误后立即中止拼接,丢弃半截残缺文本并执行带退避策略的重试。
const payload = JSON.parse(rawDataLine);
if (payload.error || payload.type === 'error') {
  throw new Error(`Mid-stream error after HTTP 200: ${payload.error?.message || JSON.stringify(payload)}`);
}

[边界说明]这是 Claude 官方文档与 OpenAI 兼容流式协议明确强调的生产级边界条件。

验证命令: node sse-stream-guard.mjs

修复方案:关闭反向代理缓冲并延长长上下文/推理模型读取超时

sol-proxy-buffering-timeout

本地模型处理万字长 Prompt 预填充(Time-to-First-Token)可能耗时 20–90 秒期间无数据帧返回,触发 Nginx 或网关默认 60 秒空闲超时切断。

  1. 在 Nginx 或反向代理配置中设置 proxy_buffering off 与 proxy_read_timeout 300s。
  2. 客户端侧将首字等待超时(TTFT Timeout)与分块空闲超时区分设置。
# Nginx location block for LLM SSE endpoints
location /v1/ {
    proxy_pass http://127.0.0.1:11434/v1/;
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 300s;
    chunked_transfer_encoding on;
}

[边界说明]若经由云端代理转发,还需注意云厂商网关默认的 100 秒无字节传输限制。

验证命令: nginx -t && sudo systemctl reload nginx

修复方案:解析结构化 JSON 前先校验 finish_reason 是否为 length 截断

sol-finish-reason-length

模型输出达到了 max_tokens(或 num_predict)上限,导致 JSON 字符串在闭合括号前被截断,但流本身正常发送了 [DONE]。

  1. 在执行 JSON.parse 前,强制检查最后一个 Chunk 的 finish_reason 是否为 "stop"。
  2. 若使用带 <think> 标签的推理模型,需预留至少 2048 max_tokens 并在解析前剥离思考块。
if (lastChunk.choices?.[0]?.finish_reason === 'length') {
  throw new Error('Output truncated by max_tokens limit before JSON closed');
}

[边界说明]即使开启了 json_object 模式,一旦触及 max_tokens 上限依然会产生未闭合的残缺 JSON。

验证命令: echo "Verified finish_reason guard"
PAIRED REPRODUCIBLE GUIDE // 配套实测指南与硬件环境基线

LLM API 在 HTTP 200 后流式报错:错误发生条件与恢复边界

为什么简单的状态码列表不足以覆盖运行中的失败:深入解析 HTTP 200 响应头发出后的 SSE 流内错误帧、反向代理超时切断与 JSON 截断恢复策略。

实测硬件与系统

Node.js 22.13+ 流式客户端 · 适用于 Claude API、OpenAI 兼容接口及本地 Ollama / vLLM

锁定版本栈

Node.js 22.13+ · Anthropic Messages SSE · OpenAI Chat Completions SSE

验证日期 (2026-10-23)

同时适用于本地与云端流式 API — 配套抗中断 SSE 解析器代码,可在本地零成本验证流内异常捕获与超时取消逻辑。