快速结论:该问题通常发生在 Dify 自托管(Docker)环境中,内置 OAuth 工具提供方(如 QuickBooks、Microsoft Excel 365)的 access token 过期后,刷新流程虽然 HTTP 返回 200,但新凭据没有写回数据库,同时多个 GraphWorker 并发刷新导致旧 refresh token 被轮换后失效。优先检查 tool_builtin_providers 表中的 updated_at 是否等于 created_at,以及日志中是否出现同一外部请求内多次连续 refresh。
适用环境:Issue 中确认的环境为 Dify v1.12.1,Docker 自托管部署;评论中另有用户报告 Dify Cloud、Microsoft Excel 365 插件出现类似现象。Issue 未提供操作系统、Python、CUDA、显卡等版本信息。
最快修复方案:暂无确认的一步修复方案。Issue 中维护者表示正在修复并运行集成测试,评论提到 #32195 可能相关,但该 PR 当时仍处于打开状态且未更新,因此不能视为已验证修复。
注意事项:在修复前,手动重新授权只能临时恢复,无法阻止凭据再次失配;频繁重授权也可能因旧 refresh token 已被轮换而继续失败。关闭 stale bot 后重新打开 Issue 的请求并未改变问题状态,因此不建议把关闭状态当作已修复证据。
问题场景
在 Dify 中配置带 OAuth2 凭据的内置工具提供方(例如 QuickBooks),并创建包含 2 个以上使用同一 OAuth 提供方的工具节点的工作流。等待 access token 过期(约 1 小时)后执行工作流,即可触发刷新逻辑。Issue 中的生产数据显示 24 小时内发生了 693 次 refresh HTTP 调用(API 容器 528 次、Worker 165 次),均返回 HTTP 200,但数据库行始终未更新。评论中另有 Microsoft Excel 365 插件用户报告约 90 天后出现 Entra ID AADSTS700082(refresh token 因不活跃过期),而该用户每天使用插件,说明刷新后的 token 可能未被正确保存。
报错原文
Bug: OAuth token refresh credentials never persist to DB + concurrent refresh race condition
02:18:24 [GraphWorker-0] refresh_credentials → SUCCESS (consumes old refresh_token)
02:18:25 [GraphWorker-0] tool/invoke → SUCCESS
02:18:28 [GraphWorker-1] refresh_credentials → FAIL "refresh token expired or revoked"
02:18:28 Node ABORT → workflow fails
05:30:44.680 [GraphWorker-0] refresh ← 3ms apart
05:30:44.683 [GraphWorker-1] refresh ← same req_id
AADSTS700082 (refresh token expired due to inactivity)
原因分析
Issue 列出了两个关键缺陷:
- 刷新后未持久化(Bug 1):
tool_manager.py中的刷新代码在更新凭据后调用了db.session.commit(),但生产数据库证据显示created_at == updated_at,且expires_at始终为创建时间加 3600 秒。由于updated_at列设置为onupdate=func.current_timestamp(),只要 ORM 提交生效就应更新该列,因此可能原因是刷新后的凭据更新没有真正进入同一会话的持久化路径,或提交未作用于预期对象。同一 greenlet 在约 1 秒内连续发起 4–6 次刷新,说明后续调用读到的仍是旧expires_at,也支持“提交未生效”的判断。 - 并发刷新无锁(Bug 2):多个
GraphWorker线程同时检测到凭据过期并调用refresh_credentials。两个 worker 读到同一份过期凭据,先到的 worker 刷新成功并轮换了 refresh token,后到的 worker 仍用旧 token 刷新,得到refresh token expired or revoked,节点随之中止。此前两次刷新相隔约 3ms 且使用同一req_id,曾因 OAuth 提供方宽限期而成功,但宽限期结束后旧 token 被拒绝,形成永久失败。
级联影响是:数据库始终保存创建时的原始 refresh token,该 token 在数百次使用后已被 OAuth 提供方作废,后续所有请求都读取无效 token 并失败,只能手动重新授权。
环境排查
- 确认 Dify 版本是否为 v1.12.1 或受影响版本,并记录部署方式(Docker 自托管或 Cloud)。
- 确认触发问题的内置工具提供方,例如 QuickBooks 或 Microsoft Excel 365 插件。
- 检查
tool_builtin_providers表中对应 provider 的created_at、updated_at、expires_at,重点看updated_at是否等于created_at。 - 检查 API 容器和 Worker 日志中同一外部请求或同一时间窗口内是否出现多次
refresh_credentials,以及是否出现refresh token expired or revoked。 - 确认 OAuth 提供方是否启用 refresh token 轮换(rotation)和宽限期策略;Issue 未列出具体依赖版本,无需补查 Python、CUDA 或显卡。
解决步骤
- 先做只读排查:执行 Issue 中给出的查询,确认目标 provider 的
updated_at是否长期等于created_at,以及expires_at是否停留在创建时间加 3600 秒。 - 在 API 与 Worker 日志中按
refresh_credentials、GraphWorker、req_id检索,确认是否存在同线程连续刷新或跨 worker 近同时刷新。 - 若已出现
refresh token expired or revoked或AADSTS700082,说明旧 refresh token 已失效,可优先尝试由管理员手动重新授权该工具提供方,先恢复业务;这只能临时缓解,不能修复持久化和并发缺陷。 - 暂不要反复高频重新授权以“维持活跃”,因为根因未修复时新 token 仍可能无法写回数据库并再次被轮换失效。
- 关注上游修复:#32195 被评论提及可能相关,但在 Issue 讨论中仍处于打开状态且未更新,尚不能确认已解决问题。Issue 中维护者表示正在修复并运行集成测试,在合并版本发布前,可优先尝试在自有部署中验证刷新后
updated_at是否变化,而不是直接修改生产数据。 - 若必须自行打补丁,应先确认补丁覆盖两点:刷新成功后新
encrypted_credentials与expires_at确实提交;并发刷新通过锁或串行化机制避免多个 worker 同时使用旧 refresh token。Issue 本身未给出已验证补丁内容,自行修改需自行承担风险。
验证方法
等待 access token 过期后再次执行工作流,然后重复查询 tool_builtin_providers:若修复生效,对应行的 updated_at 应晚于 created_at,expires_at 应推进到新的过期时间。同时检查日志:一个过期周期内同一 OAuth 提供方应只有一次成功刷新,不应出现同一请求内连续多次 refresh_credentials,也不应出现多个 GraphWorker 对同一凭据近同时刷新。若仍看到 refresh token expired or revoked,说明并发或持久化问题尚未解决。对于 Microsoft Excel 365,可观察日常使用下是否仍会在约 90 天后出现 AADSTS700082。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[ROCm][AMD] Kimi-K3 Gap and Roadmap Tracking](https://www.chat-gpts.plus/wp-content/uploads/2026/11/50682-3200efb1-768x403.jpg)

