快速结论:该报错通常出现在自托管 dify-agent run server 接入 Redis 后,调用方在 POST /runs 中传入 idempotency_key 并重放同一请求(或并发请求打到共享同一 Redis 前缀的不同进程)时发生。优先排查 RunScheduler.create_run() 是否在 run 准入阶段原子占用幂等键,而不是在写入 run 记录之后才登记键。
适用环境:Issue 已确认:Dify main 分支(base commit 59fb603)、Self Hosted (Source)、独立运行的 dify-agent run server、依赖 Redis。Python、CUDA、PyTorch、显卡等未在 Issue 中给出,不做推断。
最快修复方案:暂无确认的一步修复方案。Issue 中提出的方向是:在 Redis 中用 SET key NX PX/EX <retention> 原子认领幂等键,只有认领成功的一方生成 run id 并创建 run,认领失败的一方返回原 run id 与当前状态。
注意事项:该方案属于 Issue 中提出的设计,未在 59fb603 上验证;评论中讨论的并发压测数字来自评论者自己的复现场景,并非 Dify 调度器的实测结果。对外可见行为变更(同一键不同请求应返回 HTTP 409)需要回归测试覆盖,且不带幂等键的请求应保持原有行为。
问题场景
自托管部署下启动独立的 dify-agent run server 并配好 Redis,向 POST /runs 发送带 idempotency_key 的请求,然后用相同的键重放同一请求。预期是幂等去重,实际观察到服务器生成了新的 run id 并启动了新的后台任务。当并发请求分别落到共享同一 Redis 前缀的不同 server 进程时,也会出现同样现象。
报错原文
bug(agent): CreateRunRequest.idempotency_key is accepted but not enforced
RunScheduler.create_run() always creates a new UUID, persists a new run record,
and starts a new task. CreateRunRequest.idempotency_key is accepted but is not
used during run admission.
原因分析
已确认的原因是:CreateRunRequest.idempotency_key 只在请求模型中被接收,run 准入路径并未使用它;RunScheduler.create_run() 每次都生成新 UUID、持久化新 run 记录并启动新任务。
评论补充了两类可能原因,可作为实现排错时的关注点:
- 若采用先查询再写入(check-then-act)的方式实现去重,两个并发重放可能同时查不到记录,从而各自创建 run,仍然无法满足 at-most-once 的要求。
- 若幂等键是在 run 记录写入之后(或经过较慢的校验步骤之后)才登记,这段窗口内后来者会看到“尚未处理”而重复执行。
- 若状态查询或监控路径也会读写幂等记录,可能误“消耗”该键,导致后续真实请求被误判为重复。
环境排查
- 确认 Dify 版本与分支,Issue 对应 main 分支 base commit
59fb603,排查前先核对是否包含相关修复。 - 确认是否为 Self Hosted (Source) 部署,以及
dify-agentrun server 是否独立启动。 - 确认 run server 已正确连接 Redis,并核对多个 server 进程使用的 Redis prefix 是否一致。
- 确认
POST /runs请求中确实携带了idempotency_key,并记录重放时请求体是否完全一致。 - 确认 Agent v2 workflow、retry、cleanup 调用方是否已在生成幂等键,以便区分是调用方未传键还是服务端未生效。
解决步骤
- 在 run 准入路径(
RunScheduler.create_run()入口)加入幂等处理,而不是在 run 记录落库之后再补登记键。 - 可优先尝试评论中的原子认领思路:以
SET run:idem:<prefix>:<key> <new_run_id> NX EX <retention_window>作为认领操作。返回OK表示本请求负责创建该 run;返回nil则读取已有键,返回原 run id 及当前状态。 - 为幂等键关联规范化请求指纹(如哈希摘要),仅持久化摘要和 run id,不存储模型凭证或原始请求。
- 同一键配合不同规范化请求时返回 HTTP 409,避免静默返回不匹配的原 run。
- 保证查询/监控等只读路径读取幂等记录时不执行认领操作,仅准入路径可以认领。
- 不带幂等键的请求保持原有行为,即每次创建新 run。
- 补充回归测试,至少覆盖:完全相同的重放、并发重放、同键不同请求体、无键请求。
验证方法
在配置的运行保留窗口内,用同一幂等键和同一请求体重放 POST /runs,应返回原 run id 及当前状态,且服务端没有新增后台任务;用同一键发送不同规范化请求,应返回 HTTP 409;不带幂等键的请求仍应创建新 run。多进程并发重放同一键时,只应产生一次 run 创建。若按上述步骤实现,应同时确认只读查询路径不会消耗幂等键。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[Bug] DeepSeek Harness (deepseek-harness): model list is empty and no model picker](https://www.chat-gpts.plus/wp-content/uploads/2026/11/21173-bae2750d-768x403.jpg)
![[bug]: Z-Image, Qwen-Image and Anima LoRA converters ignore `.alpha` and drop DoRA magnitudes on lora_A/lora_B layers](https://www.chat-gpts.plus/wp-content/uploads/2026/11/10019-f156d1bf-768x403.jpg)
