[Bug]: /api/v1/dify/retrieval resets the connection (502 / “Reached max retries”) because build_error_result sets HTTP status to RetCode.ARG

当 Dify 把 RAGFlow 数据集接入为 External Knowledge API 时,Dify 会发送一个不带请求体的 POST 请求到 /api/v1/dify/retrieval,导致连接被重置(502;在 Dify 日志中表现为 Reached max retries)。优先检查

快速结论:当 Dify 把 RAGFlow 数据集接入为 External Knowledge API 时,Dify 会发送一个不带请求体的 POST 请求到 /api/v1/dify/retrieval,导致连接被重置(502;在 Dify 日志中表现为 Reached max retries)。优先检查 RAGFlow 服务端日志中是否出现 h11 的 _SWITCH_UPGRADE 报错;如果出现,基本可以确定 RAGFlow 的 build_error_result 把非 HTTP 的 RetCode 值(例如 101)直接写入了 HTTP 状态码。

适用环境:RAGFlow v0.26.1(有证据表明 main 分支同样存在该问题);docker-compose 部署;DOC_ENGINE=elasticsearch;DEVICE=cpu;Dify v1.14.x;Amazon Linux 2023。Issue 未提供 Python、CUDA、PyTorch 或显卡信息。

最快修复方案:暂无确认的一步修复方案。Issue 中尚未记录合并的修复补丁;用户建议的修复方式是把 build_error_result 中设置的 HTTP 状态码限制为合法的 HTTP 范围(非合法时回退为 400),评论者认为该思路可行,但尚未验证。

注意事项:在补丁合入之前,如果只是临时绕过,需要注意 Dify 的连接测试本身就是不带请求体的请求,可能会触发该异常,因此不能算作真正的修复。直接修改 api/utils/api_utils.py 会影响所有调用 build_error_result 的接口;在没有验证的情况下,应先在看板或测试环境中确认这些接口的返回行为。

问题场景

用户将 RAGFlow 的数据集接入 Dify,作为 External Knowledge API 使用。Dify 在连接测试时调用 http://<host>/api/v1/dify/retrieval。Dify 端的 check_endpoint_and_api_key 只携带 Authorization 头,不发送请求体;RAGFlow 端正确判定缺失 knowledge_id / query 参数并调用 build_error_result,但随后连接被重置,经过前置 nginx 或其他中间层时呈现为 502。

报错原文

failed to connect to the endpoint: http://<host>/api/v1/dify/retrieval
core.helper.ssrf_proxy.MaxRetriesExceededError: Reached maximum retries (3) for URL .../api/v1/dify/retrieval
HTTP Request: POST .../api/v1/dify/retrieval "HTTP/1.1 502 Bad Gateway"

h11._util.LocalProtocolError: Received server _SWITCH_UPGRADE event without a pending proposal
h11._util.LocalProtocolError: Can't send data when our state is ERROR
[ERROR] Error in ASGI Framework

原因分析

最可能的原因是 api/utils/api_utils.py 中的 build_error_result 把 RetCode 枚举值直接赋给了 response.status_code。RetCode 同时包含标准 HTTP 码(如 BAD_REQUEST=400、NOT_FOUND=404、SERVER_ERROR=500)和自定义应用级码(EXCEPTION_ERROR=100、ARGUMENT_ERROR=101、DATA_ERROR=102、AUTHENTICATION_ERROR=109)。当传入 ARGUMENT_ERROR 时,响应状态码变成 HTTP 101 Switching Protocols;Hypercorn/h11 因为没有客户端发起的升级请求而判定为协议异常,于是中止连接,上游断开后表现为 502。凡是使用非标准 HTTP 状态码的 RetCode,都可能触发同样的问题。

环境排查

  • 确认 RAGFlow 版本(Issue 中确认 v0.26.1,且 main 分支同样存在)
  • 确认 Dify 版本(Issue 中为 v1.14.x)及 External Knowledge API 配置的端点与 API Key
  • 确认部署方式与中间层:docker-compose、DOC_ENGINE=elasticsearch、DEVICE=cpu,以及是否存在前置 nginx 或其他反向代理
  • 查看 RAGFlow 服务端日志中是否出现 _SWITCH_UPGRADE 或 Can’t send data when our state is ERROR
  • Issue 未涉及 Python、CUDA、PyTorch、显卡等版本,暂不需要作为排查项

解决步骤

  1. 先进行复现确认:使用有效 API Key,向 http://<host>:9380/api/v1/dify/retrieval 发送不带请求体的 POST 请求,观察是否出现连接重置或空响应;同时查看服务端是否输出 h11 报错。
  2. 作为对照,再发送一次带有效 JSON 请求体的 POST 请求;如果返回 HTTP 200,则说明问题只出现在参数错误分支,符合本 Issue 的判断。
  3. 可优先尝试(尚未在上游验证):检查 api/utils/api_utils.py 中 build_error_result 对 status_code 的赋值。Issue 建议在赋值前做范围校验,仅在 200–599 之间使用该值,否则回退为 400,并保留 JSON 体中原始的 code。
  4. 评论者提出的替代思路(同样未验证):为 RetCode 建立显式的 HTTP 映射,例如 ARGUMENT_ERROR→400、AUTHENTICATION_ERROR→401、EXCEPTION_ERROR→500,同时在响应体中保留原始 RetCode。
  5. 关注上游修复进展。Issue 提到 #14169、#13860、#15028、#15652 等 PR 改进了该端点的错误处理一致性,但没有解决把非标准状态码用作 HTTP 状态码这一根本问题,因此升级前需要确认目标版本是否已经包含相应修复。

验证方法

重新执行不带请求体的 POST 请求,预期得到正常的 HTTP 错误响应(如 400),或返回 200 且响应体中包含 {“code”: 101, …},而不再出现连接重置或 502;服务端日志中不再出现 h11 的 _SWITCH_UPGRADE 或 Can’t send data when our state is ERROR。随后在 Dify 中重新执行 External Knowledge API 的连接测试,确认不再报 Reached max retries / 502。

参考来源

infiniflow/ragflow #16208

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 34298

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注