ComfyUI died on kai-tower-3026 and the scheduled task did not restart it, leaving Tailscale Serve forwarding to a dead listener #883
Labels
No labels
burndown-2026-06
burndown-2026-08
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/advocate
role/director
role/exec
role/frontend
role/gamedev
role/human
role/platform
role/qa
role/science
role/sysadmin
state
ambient
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/infrastructure#883
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom
Observed on
kai-tower-3026at 2026-08-19 21:56 PDT:3221225786is0xC000013A,STATUS_CONTROL_C_EXIT. The process was terminated rather than crashing on its own fault.comfyui.logends on a clean, successful startup about 75 minutes earlier:Nothing follows. Log mtime is 2026-08-19 20:42:40 PDT.
Two separate problems
1. The scheduled task did not restart it.
docs/comfyui-runtime.md:12states "a user-level scheduled task restarts ComfyUI after failure and at logon". The task exists and registers fine, but its state isReadyand the runtime has stayed down. Either the restart trigger is not configured the way the doc claims, orSTATUS_CONTROL_C_EXITis not a condition it treats as failure. Worth checking whatRegister-ScheduledTaskinscripts/converge-comfyui-windows.ps1around line 434 actually sets for restart-on-failure, against what the doc promises.2. Tailscale Serve keeps forwarding to the corpse. Serve is converged and healthy:
With nothing bound to loopback 8188, the tailnet endpoint still accepts at the TCP layer and then fails. That is the worst shape for a caller.
coilyco-bridge/agentic-os-xxxpoints itsComfyUIBackendat this endpoint, so an AOSX run against the tower gets a connection-level failure rather than a clear "runtime is down".docs/comfyui-runtime.md:79says "when the tower is off, the ser8 gateway stays up and returns a clear proxy failure instead of wedging the rollout". That covers the tower being off. It does not cover the tower being on with the process dead, which is the state observed here.What is not wrong
Ruling out the usual suspects, since all of these check out:
0.29.0on disk matches the pin.comfy-aimdo inited for GPU: NVIDIA GeForce RTX 3090 Ti (VRAM: 24563 MB)andDynamicVRAM support detected and enabled.This is purely a supervision and liveness gap, not a convergence gap.
Do
STATUS_CONTROL_C_EXITon a scheduled task usually means an explicit stop, a logoff, or a console-attached parent going away, so start there.Live access required
This needs someone on
kai-tower-3026to inspect Task Scheduler history and the Windows event log. Labeledautonomy/live-collabfor that reason. A sealed engineer clone cannot observe any of it.Related: infrastructure#882 (the
-WhatIfpreview bug found on the same pass), infrastructure#763 (AOSX Qwen Edit live verification, which needs this runtime up to proceed).Warning before anyone runs the converge again
Someone ran
windows-comfyui-convergeon the tower at 22:18 on 2026-08-19, apparently to fix this issue. It took the endpoint from working to dead. Please read the timeline below before running it again. The runtime is down as of 22:26:21 PDT.Timeline from the Task Scheduler operational log
The converge stopped the healthy 22:11 instance and registered a replacement that exited successfully in under one second and never started the runtime.
comfyui.loghas had no new content since 22:12:25 and still shows six lifetimeStarting serverentries, so the 22:18:23 launch produced no startup at all.What answered my 22:22:46 check was the orphan: the python child of the 22:11 instance outlived its stopped parent and kept serving for about five more minutes. That is why this looked briefly fixed. It was not.
Root cause is now concrete
The task is configured like this:
Restart-on-failure is configured, five retries a minute apart. It never fires because the instance reports success. The action returns immediately with code 0 rather than staying attached to the runtime, so from Task Scheduler's point of view a dead ComfyUI is indistinguishable from a completed job. There is nothing for it to restart.
That upgrades the original finding here. It is not that the restart policy is missing. It is that the supervision contract is wrong:
serve.cmdis invoked in a way that detaches, so the task's liveness signal is meaningless.docs/comfyui-runtime.md:12promising "restarts ComfyUI after failure" cannot hold under this shape.It also explains the 20:42 death recorded in the issue body. Same signature, no log output on exit,
Readystate afterward, no restart.Suggested direction
Whatever the fix, it needs the task action to remain attached for the runtime's lifetime so a death is an instance failure, or it needs a separate liveness check that can trigger recovery. A logon-only trigger with no periodic re-check means an unattended tower stays down until the next logon regardless.
Also worth deciding what the tailnet edge should do meanwhile. Right now
tailscaledholds 8188 on both tailnet addresses with nothing on127.0.0.1:8188behind it, so callers get a connect-time failure rather than a clear signal.Note on attribution
Events 140 and 330 name user
kai-tower-3026\firem, which is the account every local process runs as, so the log cannot distinguish a human from an agent. Six Claude sessions were running on the box at the time. No branch or commit touches the ComfyUI files, so this was an operational run rather than a code change.Correction to my previous comment, and the runtime is back up
I called the root cause wrong above. New evidence contradicts it, and it changes what the fix should be.
Current status, 22:30:35 PDT: ComfyUI is UP. Loopback listener present,
/system_statsreturns 200, task stateRunning,LastTaskResult=267009which isSCHED_S_TASK_RUNNING. It started at 22:27:06 andcomfyui.logrecorded its 7thStarting serverat 22:27:21.What I got wrong
I wrote that "the action returns immediately with code 0 rather than staying attached to the runtime" and concluded the supervision contract was fundamentally broken. That is not right. The 22:27:06 instance has stayed attached and
Running, and the 22:11:43 instance stayed attached for about seven minutes before it was stopped.serve.cmdis untouched since 2026-07-29, so the converge never rewrote it. The action supervises correctly under normal conditions.What actually happened at 22:18
A restart race, not a supervision defect:
cmd.exebut left the child python alive still holding127.0.0.1:8188.return code 0in one second, with nothing written to the log.So the defect is in the converge's "restart under scheduled-task ownership" step: it does not wait for the previous process to release the port before relaunching. The window between stop and start is about four seconds, and the orphan outlived it by roughly five minutes.
What still stands from the original report
3221225786(0xC000013A), which is non-zero, so the five-retry restart policy should have fired and did not bring it back. That part of this issue is unexplained and still open.Revised suggestion
Two separable fixes:
127.0.0.1:8188to be free, or stop the process tree rather than just the task instance, before relaunching. Right now running the converge against a live runtime reliably leaves it down for several minutes.Apologies for the noise in the prior comment. The warning at the top of it still holds for reason 1, just not for the reason I gave.
Both open items resolved, and the second one traces to #888
Third update, and it supersedes my root-cause claim in both earlier comments. Runtime is UP as of 22:45.
Item 2, why restart-on-failure did not recover the 20:42 death: it never could
The restart policy is not broken. There was no task instance to restart. Every ComfyUI start and stop event in the Task Scheduler operational log for the last seven days:
The task started exactly three times in seven days, all tonight, the first at 22:11:43. Nothing at 20:42. So the runtime that died at 20:42 was never task-owned, there was no instance failure, and restart-on-failure had nothing to act on. The
LastTaskResult=3221225786I quoted in the issue body was a stale value from a run older than seven days, not evidence of a 20:42 failure.The real pattern is orphaned runtimes that no task owns. Nothing recovers those, by design.
Item 1, the restart race: the kill was never landing
The converger's orphan-kill filtered on
Win32_Process.ExecutablePathandCommandLine. Both read back empty for the live runtime, so the filter could not match and the orphan always survived the restart. Measured:Fixed in #890 by matching on loopback port ownership and waiting for the port to clear.
Why those fields are empty, and this is the part that needs a decision
The runtime is in session 0. The converger runs unelevated in session 1, so it cannot read that process's details or signal it.
This traces to #888, merged 22:41:38 PDT today, which set the ComfyUI task principal to
-LogonType S4U. S4U runs in session 0. Before it, the task wasInteractive, the runtime shared the operator's session, and the original filter would have had the visibility it needed. The 22:18 converge that took the endpoint down was running that branch, which is why event 140 re-registered the task and why the 22:27 launch landed in session 0.So #888 removed the converger's ability to manage its own runtime. #890 makes that a loud, self-diagnosing failure that leaves a working runtime alone instead of a dead endpoint, but it deliberately does not paper over the placement itself.
What is left to decide
Three options, and it belongs to whoever owns #888 since the tradeoff that motivated S4U is theirs:
Stop-Processand drive it entirely through the task surface, so session never matters.Until one lands, a converge against a live ComfyUI cannot restart it, and will now say so rather than breaking it.
Retracting my item 2 conclusion: the evidence window was 72 minutes, not 7 days
Fourth update. My previous comment claimed the restart policy "never could" have fired because the task started only three times in seven days. That claim is unsupported and I am withdrawing it.
The Task Scheduler Operational log does not cover the period I queried:
Oldest retained event is 22:03:02. My seven-day query silently returned only what existed, which was everything from 22:03 onward. "Three starts in seven days" was really "three starts in the last 72 minutes." The 20:42 window is simply not covered, so the log says nothing either way about whether a task instance was running then.
Note the record count is 498 against a 16 MB cap, so this did not roll from volume. The channel looks to have been cleared around 22:03.
What the session log does show
This reorders the night. The logoff at 20:24:58 is what reaped the Interactive-bound task. The 20:41:09 logon then re-fired the logon trigger and started ComfyUI, which is what wrote the clean startup ending at 20:42:40. So 20:42 was a birth, not a death, and the
LastTaskResult=3221225786I recorded in the issue body came from the instance killed at 20:24:58.That also means the outage I opened this issue about ran from roughly 20:24:58, briefly interrupted by a runtime that came up at 20:41 and died again before my 21:56 check.
What is actually still unknown
ComfyUI died between 20:42:40 and 21:56 with no further log output and no intervening session event in the TerminalServices log. Under the Interactive principal in force at the time, a session change is the expected killer, and there wasn't one. That is the residual mystery here and I have no evidence for it. The task-side record that might have explained it is gone.
Summary of what survives from this issue
Confirmed and fixed or in flight:
ExecutablePathandCommandLineread empty. Fixed in #890.Withdrawn:
Standing question for #888: whether the converger regains a way to manage an S4U task, or the task moves back to a principal it can manage. That tradeoff is yours.
Apologies for the churn on this one. Three of my four comments here have corrected the previous one, which is a sign I was reasoning ahead of the evidence rather than checking retention before drawing conclusions from a log.
The runtime that is up right now is an orphan, and the converger made it one
Fifth update, from a live session on the tower at 23:30 PDT. This is not another correction of the timeline. It is a currently-reproducing state that none of the comments above record, and it has a concrete cause in the code #890 landed.
Measured now
ComfyUI answers. The task that owns it does not exist. Python 39132 was created at 22:27:06 by task instance
a4d64f2b, its parentcmd.exe42056 is gone, and the task has beenReadysince.What killed the parent
The operational log now carries events past the window quoted above:
No
id=140, so no task definition was rewritten at 22:41:28. This was a bareStop-ScheduledTask, not a re-register. #888 merged at 22:41:40, twelve seconds later.The cause, in the code merged by #890
scripts/converge-comfyui-windows.ps1, the$restartNeededblock:The stop runs before the manageability check. So on an S4U host, where
Stop-LoopbackListeneris guaranteed to be refused, the sequence is: detach the runtime from its supervisor, discover the port cannot be freed, then throw a message saying the runtime was left alone.It was not left alone. It was orphaned. The message is the opposite of what happened, which is why this passed as a safe failure.
#890 correctly stopped the converger from killing the endpoint. The remaining defect is quieter and worse: the endpoint stays up, the operator reads "left alone" and believes nothing changed, and the runtime is now unsupervised. Task state is
Ready, so there is no instance forRestartCount=5to act on, and the only trigger is logon. The next time this python exits, nothing brings it back until someone logs in.Fix
Prove the port can be freed before detaching anything:
Stop-ScheduledTaskstill has to run beforeStart-ScheduledTask, becauseMultipleInstances IgnoreNewwould drop the new start while the old instance lives. Moving it after the guard keeps that ordering and makes the throw honest.This fix is correct under all three of the #888 options, so it does not need that decision to land first.
On the orphan pattern generally
This is where the "orphaned runtimes that no task owns" in the third comment come from. They are not mysterious. A converge run against a live S4U runtime manufactures one every time, and reports success at leaving it alone.
Whether the same mechanism produced the original 20:42 to 21:56 outage is not something this evidence settles. That was pre-#890 code and the operational log still starts at 22:03:02. The shape is consistent. I am not claiming it as proven.
Recovering the current state needs a human
The live runtime cannot be re-adopted from an unelevated session. I am
kai-tower-3026\firem, session 1,elevated=False, and pid 39132 is session 0, soStop-Processis refused.Start-ScheduledTaskon its own would make it worse: the new instance's python would fail to bind the port the orphan still holds and exit in under a second, which is exactly the 22:18:23 signature.Recovery is: stop pid 39132 from an elevated context, confirm 8188 is free, then
Start-ScheduledTask -TaskName ComfyUI. That drops the endpoint for the length of a ComfyUI startup, so it wants a person who knows nothing is mid-render.Also still standing
Tailscale Serve holds 8188 on both tailnet addresses regardless of what is behind it. Right now that is harmless because the orphan answers. It is the same connect-time-failure shape the moment the orphan exits, and nothing about this changes that.