comfyui_api backend with AllowIdle can never recover from IDLE back to RUNNING (stale reused CancellationToken)

用户在使用 SwarmUI 的 comfyui_api Backend(ComfyUI API By URL)时,开启了 AllowIdle 功能(允许后端在空闲时暂停)。当远程 ComfyUI 进程因崩溃或网络抖动等原因短暂失联后,该后端进入 IDLE 状态,随后即使远程 ComfyUI 实例已恢

comfyui_api backend with AllowIdle can never recover from IDLE back to RUNNING (stale reused CancellationToken)

comfyui_api backend with AllowIdle can never recover from IDLE back to RUNNING (stale reused CancellationToken)

快速结论:该报错出现在 SwarmUI 使用 comfyui_api(通过 URL 远程连接 ComfyUI)后端且开启了 AllowIdle 功能时。后端一旦进入 IDLE 状态后永远无法自动恢复为 RUNNING,即使远程 ComfyUI 实例完全正常。优先排查 Idler.ValidateCall 闭包中是否复用了过期的 CancellationToken

问题场景

用户在使用 SwarmUI 的 comfyui_api Backend(ComfyUI API By URL)时,开启了 AllowIdle 功能(允许后端在空闲时暂停)。当远程 ComfyUI 进程因崩溃或网络抖动等原因短暂失联后,该后端进入 IDLE 状态,随后即使远程 ComfyUI 实例已恢复健康(/object_info 端点正常响应),后端也永远不会自动切换回 RUNNING 状态。后端卡片一直显示 “idle”,且永远不会被选中执行任务(BackendHandler.cs 要求 Status == BackendStatus.RUNNING),导致任务被静默丢弃。

报错原文

Once such a backend enters IDLE status for any reason (e.g. the remote ComfyUI process crashed and was restarted),
it never automatically returns to RUNNING, even though the remote ComfyUI instance is confirmed healthy and
its /object_info endpoint responds quickly and successfully.

The backend is permanently excluded from generation dispatch (BackendHandler.cs requires Status == BackendStatus.RUNNING
to select a backend for a job), so it silently never receives any work again — with no error surfaced to the user
beyond the backend card showing "idle."

原因分析

问题根源在于 src/BuiltinExtensions/ComfyUIBackend/ComfyUIAPIAbstractBackend.cs 中,Idler.ValidateCall 闭包捕获了一个在初始化时创建的、一次性自过期CancellationTokenSource

if (CanIdle)
{
    Idler.Backend = this;
    using CancellationTokenSource cancel = Utilities.TimedCancel(TimeSpan.FromMinutes(1));
    Idler.ValidateCall = () => SendGet<JObject>("object_info", cancel.Token).Wait();
    Idler.Start();
}

cancel 的 1 分钟定时器从后端初始化时开始计时。当后端初始化超过 1 分钟后(实际场景中几乎必定如此,因为空闲触发事件通常发生在初始化之后很久),这个 token 已进入过期状态。后续 IdleMonitor(每 5 秒轮询一次)执行的每次 ValidateCall() 都会因为 token 已过期而立即失败,进入 catch 分支,导致后端永远无法被标记为 RUNNING

另外,using 关键字会在方法返回时立即释放该 CancellationTokenSource,这是一个相关的但独立的问题——一个已经被释放的 CTS 被长时间运行的闭包无限复用。但即使不考虑释放问题,仅 1 分钟自过期本身就足以导致永久性失败。

环境排查

  • SwarmUI 版本:确认 Issue #1450 修复版本(建议升级到最新版)
  • 确认后端类型是否为 comfyui_api(ComfyUI API By URL)
  • 确认 AllowIdle 功能是否已启用
  • 确认远程 ComfyUI 实例是否实际可达(/object_info 端点是否正常响应)
  • 确认后端初始化时间是否已经超过 1 分钟(绝大多数场景都满足)

解决步骤

  1. 升级 SwarmUI:首先确认是否已包含 Issue #1450 的修复(修复方式见下方步骤 2)。如不确定,直接升级到最新版本。
  2. 代码级修复(手动 patching):打开 src/BuiltinExtensions/ComfyUIBackend/ComfyUIAPIAbstractBackend.cs,找到 if (CanIdle) 代码块,将原来的 ValidateCall 闭包改为在每次调用时生成新的 CancellationTokenSource
    if (CanIdle)
    {
        Idler.Backend = this;
        Idler.ValidateCall = () =>
        {
            using CancellationTokenSource cancel = Utilities.TimedCancel(TimeSpan.FromMinutes(1));
            SendGet<JObject>("object_info", cancel.Token).Wait();
        };
        Idler.Start();
    }
  3. 重启 SwarmUI 服务:应用代码修改后重新启动 SwarmUI,让修改生效。
  4. 替代方案:官方维护者指出 comfyui_api 后端“基本未测试,几乎从不正确使用”。推荐改用 comfy selfstart(本地 ComfyUI)或 swarm api(远程 ComfyUI 连接)模式,这些是经过测试的正确路径。

验证方法

创建或编辑一个 comfyui_api 后端并启用 AllowIdle,让其进入 IDLE 状态(例如手动断开远程 ComfyUI 再恢复)。确认后端在健康检查通过后能自动恢复为 RUNNING 状态,且后端卡片正常显示 RUNNING,能够被选中执行任务。

另一种验证方式:检查日志,确认 IdleMonitor 不再因为 token 过期而反复报错,且 ValidateCall() 能够正常执行。

参考来源

mcmonkeyprojects/SwarmUI #1450 — comfyui_api backend with AllowIdle can never recover from IDLE back to RUNNING (stale reused CancellationToken)

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

celebrityanime
celebrityanime
文章: 14629

发表回复

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