
Bug: WorkflowNotFoundError and _AppNotFoundError use printf-style args in resume_workflow_execution
快速结论:这个报错通常发生在 Dify 的自托管 Docker 环境中,当工作流或应用缺失时,resume_workflow_execution 函数会抛出 WorkflowNotFoundError 或 _AppNotFoundError,但错误信息中的占位符 %s 未被替换为实际值,导致日志和监控显示的是元组形式的原始报错,而非可读的格式化字符串。优先排查 api/tasks/async_workflow_tasks.py 文件中 resume_workflow_execution 函数内的异常抛出方式。
问题场景
用户在自托管(Docker)部署的 Dify 环境中,运行工作流并触发 resume_workflow_execution 时,如果相关的工作流或应用记录在数据库中不存在,代码会抛出异常。问题出现在 Dify 1.14.x 版本或 main 分支上。
报错原文
WorkflowNotFoundError(
"Workflow not found: workflow_run_id=%s, workflow_id=%s",
workflow_run.id,
workflow_run.workflow_id,
)
# 实际 str(exc) 的输出为:
('Workflow not found: workflow_run_id=%s, workflow_id=%s', 'wr-1', 'wf-2')
# 类似问题也存在于 _AppNotFoundError 的抛出位置:
_AppNotFoundError(
"App not found: app_id=%s, workflow_run_id=%s",
app_id,
workflow_run_id,
)
原因分析
根本原因在于 WorkflowNotFoundError 和 _AppNotFoundError 都是普通的 Exception 子类,没有自定义 __init__ 方法。Python 会将所有位置参数原样存储在 exc.args 元组中,不会自动执行 printf 风格的字符串格式化。因此,当 resume_workflow_execution 函数将格式字符串和参数值分开传递时,str(exc) 返回的是包含 %s 占位符和参数的元组,而不是期望的已完成插值的单一字符串。这导致日志和 Sentry 事件中的错误信息难以阅读和搜索。
环境排查
- Dify 版本:1.14.x 或 main 分支(Issue 中确认受影响)
- 部署方式:自托管(Docker)环境
- 受影响文件:
api/services/errors/app.py(定义WorkflowNotFoundError和_AppNotFoundError)以及api/tasks/async_workflow_tasks.py(抛出这些异常的位置)
解决步骤
- 定位代码文件
api/tasks/async_workflow_tasks.py中resume_workflow_execution函数(大约在第 239-247 行)。 - 修改抛出
WorkflowNotFoundError的代码行,将格式字符串和参数值合并成一个完整的字符串再传递给异常构造器。推荐使用 f-string 或显式的 % 运算符进行插值: - 方案一(推荐,代码更清晰):使用 f-string:
raise WorkflowNotFoundError( f"Workflow not found: workflow_run_id={workflow_run.id}, workflow_id={workflow_run.workflow_id}" ) - 方案二:使用 % 运算符:
raise WorkflowNotFoundError( "Workflow not found: workflow_run_id=%s, workflow_id=%s" % (workflow_run.id, workflow_run.workflow_id) ) - 对同一函数中抛出
_AppNotFoundError的代码位置执行相同的修改:raise _AppNotFoundError( f"App not found: app_id={workflow_run.app_id}, workflow_run_id={workflow_run.id}" )或者:
raise _AppNotFoundError( "App not found: app_id=%s, workflow_run_id=%s" % (workflow_run.app_id, workflow_run.id) ) - 保存修改后,重启 Dify 服务(如
docker compose restart或systemctl restart dify-api,取决于你的部署方式)。
验证方法
触发一个会导致工作流或应用缺失的场景(例如,手动删除数据库中相关的工作流运行记录,然后尝试恢复执行)。检查日志或直接捕获异常并打印 str(exc),确认输出的错误信息是一个单一的、已完成格式化的字符串,例如 "Workflow not found: workflow_run_id=wr-1, workflow_id=wf-2",而不是包含 %s 占位符的元组格式。



