快速结论:这通常发生在多个步骤同时等待人工输入、并在同一批里通过 handler.ctx.send_event 从运行外部回传事件的场景:工作流会在外部事件尚未被取走前误发 WorkflowIdleEvent,此时保存的上下文会丢失该事件。优先排查空闲判定(idle check)是否只覆盖了运行内部发出的事件,而没有覆盖 receive_queue 中尚未被取走的外部事件。
适用环境:Issue 已确认:LlamaIndex llama-index-workflows 2.26.1,以及 run-llama/llama-agents 在 main 分支的 a457071;Python 3.12;Linux。
最快修复方案:暂无确认的一步修复方案。Issue 已关闭为 #23515 的重复项;据维护者说明,run-llama/llama-agents#808(为 #23515 提交)同样修复了此处的复现脚本,可优先尝试升级到包含该修复的依赖版本。
注意事项:Issue 中未给出确认修复的发布版本号;升级前需自行确认所依赖的 workflows 与 llama-agents 版本是否包含修复。此外,上下文在错误 idle 时刻被保存后的“丢事件”属于数据层面的问题,升级后对旧存档的兼容性未经证实。
问题场景
使用 LlamaIndex 的 workflows(即 llama-index-workflows)构建带人工介入(human-in-the-loop)的工作流:两个步骤同时通过 wait_for_event 挂起等待人类回复;应用侧用 handler.ctx.send_event 一次性回传多个 HumanResponseEvent,其中 first 的回复会让所属步骤再询问一个后续问题(follow-up)。此时事件流会报告后续问题并随即发出 WorkflowIdleEvent,而第二个回复实际上还没有被送达。若应用利用 idle 事件保存上下文(handler.ctx.to_dict()),恢复后工作流会一直等待一个早已发送过的回复,永远无法结束。
报错原文
[Bug]: `WorkflowIdleEvent` is emitted while events sent from outside the run with `handler.ctx.send_event` are still undelivered, so a context saved at that idle loses them
原因分析
Issue 正文指出:_check_idle_state 的设计前提是“没有任何已发送或已返回的事件仍在等待被投递”时才判定为空闲,而 state.deliveries 只追踪从运行内部发出的事件(此部分已由 #23583 处理)。从运行外部经 ExternalContext.send_event 发送的事件会进入适配器的 receive_queue,控制循环每次 tick 只从队列中取一个,因此“已在队列中但仍未被取走”的事件对空闲判定不可见,可能被提前判定为空闲。
维护者后续在评论中确认,此问题与 #23515 为同一根因(重复 issue),并为 #23515 提交的 run-llama/llama-agents#808 可同时修复本复现脚本。结合复现结果推测,根因可能是:外部事件入队后、被控制循环取出投递前,存在一个时间窗口,空闲检查未把队列中的待取事件计入判断。
环境排查
- 确认
llama-index-workflows版本是否为 2.26.1 或更早的受影响版本。 - 确认是否通过
handler.ctx.send_event而非运行内部ctx.send_event投递事件;前者走ExternalContext与receive_queue,正是本问题涉及的路径。 - 确认是否依赖
WorkflowIdleEvent做上下文持久化;该事件被提前触发是本问题的直接表现。 - 确认 Python 版本(Issue 为 3.12)与操作系统运行环境是否与复现一致,排查时可先排除环境差异。
- 若同时集成 run-llama/llama-agents,记录所用 commit(Issue 为 a457071),便于对照 #808 修复是否已合入。
解决步骤
- 先用 Issue 中的最小复现脚本确认本地表现:运行后打印
waiters still open in the saved context,若保存时仍有未决 waiter(如other),说明命中了该竞态。 - 在
handler.ctx.send_event之后立即调用handler.ctx.to_dict()的场景中,暂时避免仅凭单个WorkflowIdleEvent就做持久化;可优先尝试在保存前用collected_waiters中未解析项做二次校验(此方案为规避手段,Issue 未验证)。 - 可优先尝试升级到包含 run-llama/llama-agents#808 对应修复的依赖版本,并重新运行复现脚本,确认 idle 前
other的 waiter 已不再悬空。 - 若仍需在旧版本运行,考虑将同时回传的多个外部事件改为间隔投递(例如等第一个回复引发的后续问题出现后再发送第二个),以缩小队列待取窗口(此方案为推测性规避,未经验证)。
- 跟踪上游 #23515 与 #808 的合入与发布状态,修复发布后再移除临时规避逻辑。
验证方法
重新执行复现脚本:在保存上下文之前,两个问题都已被报告,并且空闲事件之后 waiting(saved) 为空——即保存的上下文中没有 resolved_event 为 None 的 collected_waiters;随后从保存的上下文恢复并作答,工作流能正常走完并返回 StopEvent(result="done"),而非永久挂起等待已发送的回复。
参考来源
关联:#23515(重复根因)、run-llama/llama-agents#808(对应修复)、#23583(此前对运行内部事件投递追踪的修复)。
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


