WaterCrawl credential validation leaks JSONDecodeError for non-JSON error responses

用户在使用 Dify 的数据源功能,配置并验证 WaterCrawl 凭据时触发。具体操作是在 Dify 中为 WaterCrawl 数据源设置认证凭据,WaterCrawl 服务端返回了非 JSON 格式的错误响应(如 HTTP 401 状态码,响应体为纯文本)。

WaterCrawl credential validation leaks JSONDecodeError for non-JSON error responses

WaterCrawl credential validation leaks JSONDecodeError for non-JSON error responses

快速结论:当 WaterCrawl 的认证端点返回非 JSON 格式的错误响应体(例如 HTTP 401 并返回纯文本 “Not JSON”)时,Dify 中的 WatercrawlAuth.validate_credentials() 会因未处理的 JSONDecodeError 而抛出原始 Python 异常,而非返回友好的认证错误信息。优先排查 api/services/auth/watercrawl/watercrawl.py_handle_error 方法的 JSON 解析逻辑,并考虑添加 try/except 回退机制。

问题场景

用户在使用 Dify 的数据源功能,配置并验证 WaterCrawl 凭据时触发。具体操作是在 Dify 中为 WaterCrawl 数据源设置认证凭据,WaterCrawl 服务端返回了非 JSON 格式的错误响应(如 HTTP 401 状态码,响应体为纯文本)。

报错原文

json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

或 Python 中对应的 JSONDecodeError 异常堆栈,该异常掩盖了实际的 HTTP 状态码和错误信息。

原因分析

可能原因:Dify 的 WaterCrawl 认证流程中,_handle_error() 方法(位于 api/services/auth/watercrawl/watercrawl.py 第 38-46 行)在解析非 2xx 状态码的响应时,使用了 response.json()json.loads(response.text) 方式解析响应体。如果响应体不是有效的 JSON 格式(例如纯文本 “Not JSON”),则会直接抛出未捕获的 JSONDecodeError,而不是返回包含 HTTP 状态码的可读错误消息。

环境排查

  • 确认 Dify 的版本为 main branch at b6b9165d 或包含该 commit 的版本。
  • 确认环境是 Dify Cloud 或 Self Hosted(Source 方式部署)。
  • 检查 WaterCrawl API 服务端响应头中 Content-Type 是否为 application/json(否则可能导致非 JSON 响应体)。
  • 对比兄弟服务 Firecrawl 的认证实现,它已包含对非 JSON 响应体的回退处理。

解决步骤

  1. 定位 Dify 源代码中的 api/services/auth/watercrawl/watercrawl.py 文件。
  2. 找到 _handle_error 方法,该方法对 HTTP 状态码分别进行判断:
    • 对状态码 402、409、500 的处理中,调用了 response.json()(第 40 行)。
    • 对其他非预期状态码的处理中,调用了 json.loads(response.text)(第 44 行)。
  3. 将这两个 JSON 解析调用包裹在 try/except 块中,捕获 JSONDecodeError 或通用的 Exception,并在异常时回退到使用原始响应文本作为错误信息。可优先尝试以下修复逻辑:
    def _handle_error(self, response):
        if response.status_code in {402, 409, 500}:
            try:
                error_message = response.json().get("error", "Unknown error occurred")
            except Exception:
                error_message = response.text or "Unknown error occurred"
            raise Exception(f"Failed to authorize. Status code: {response.status_code}. Error: {error_message}")
        else:
            if response.text:
                try:
                    error_message = json.loads(response.text).get("error", "Unknown error occurred")
                except (json.JSONDecodeError, ValueError):
                    error_message = response.text
                raise Exception(f"Failed to authorize. Status code: {response.status_code}. Error: {error_message}")
            raise Exception(f"Unexpected error occurred while trying to authorize. Status code: {response.status_code}")
  4. 保存修改并重新部署 Dify 实例。

验证方法

重新触发 WaterCrawl 凭据验证,确保 WaterCrawl 服务端返回非 JSON 格式的 HTTP 错误响应(例如 401 状态码 + 纯文本 “Not JSON”)。此时不再抛出原始的 JSONDecodeError,而是显示包含 HTTP 状态码和原始响应文本的友好错误信息,例如:“Failed to authorize. Status code: 401. Error: Not JSON”。

参考来源

langgenius/dify #37497

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

celebrityanime
celebrityanime
文章: 9277

发表回复

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