fix(comfyui): stop the converger orphaning the runtime it cannot kill #892

Merged
coilysiren merged 1 commit from fix/comfyui-orphan-on-refused-restart into main 2026-08-20 06:43:09 +00:00
Owner

Closes #883 in part. The S4U placement question in #888 is untouched and still open.

The defect

The restart guard added in #890 runs Stop-ScheduledTask before it checks whether the loopback port can be freed:

Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
$refused = Stop-LoopbackListener -Port $Port
if (-not (Wait-LoopbackPortFree -Port $Port -TimeoutSeconds 30)) {
  throw "... no restart was attempted and the running ComfyUI was left alone."
}

On an S4U host the kill is always refused. The runtime sits in session 0, the converger runs unelevated in session 1, so Stop-Process cannot touch it. The sequence becomes: detach the runtime from its task, discover the port is still held, then throw a message claiming the runtime was left alone.

It was not left alone. It was orphaned. That false message is why this reads as a safe failure.

Why it matters more than the failure it replaced

#890 fixed the loud symptom, where a converge left a dead endpoint. What is left is quieter. The endpoint keeps answering, so an operator reads "left alone" and believes nothing changed, while the task sits Ready with no instance for RestartCount=5 to act on and a logon-only trigger. The next time that python exits, nothing recovers it until someone logs in.

This is also where the orphaned runtimes in #883 come from. A converge run against a live S4U runtime manufactures one every time and reports success at leaving it alone.

Measured on kai-tower-3026, 23:30 PDT

task \ComfyUI  : State=Ready  LastTaskResult=0x41306 (SCHED_S_TASK_TERMINATED)
                 LastRunTime=22:27:06  trigger=logon only
127.0.0.1:8188 : LISTEN pid 39132 python sessionId=0
  CreationDate : 22:27:06   (task instance a4d64f2b)
  ParentPid    : 42056      cmd.exe, gone since the 22:41:28 stop
/system_stats  : HTTP 200

Serving normally, supervised by nothing.

The change

Free the port first. A refused kill now leaves a supervised runtime and a throw that is true. Stop-ScheduledTask moves below the guard and stays ahead of the start, because MultipleInstances IgnoreNew would drop a start while the old instance is alive.

Four lines moved, no behavior change on a host where the kill succeeds.

Verification

Parsed clean with [Parser]::ParseFile (3215 tokens). PSScriptAnalyzer is not installed on this host.

Not verified live, deliberately. Exercising this path means running the converger against the live ComfyUI, which is the destructive act at the center of #883. The current runtime is already an orphan and recovering it needs an elevated stop of pid 39132 before Start-ScheduledTask, which drops the endpoint for one startup. That wants an operator who knows nothing is mid-render, so I left it for a live-collab pass rather than doing it to prove a four-line reorder.

Closes https://forgejo.coilysiren.me/coilyco-flight-deck/infrastructure/issues/883 in part. The S4U placement question in #888 is untouched and still open. ## The defect The restart guard added in #890 runs `Stop-ScheduledTask` before it checks whether the loopback port can be freed: ```powershell Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue $refused = Stop-LoopbackListener -Port $Port if (-not (Wait-LoopbackPortFree -Port $Port -TimeoutSeconds 30)) { throw "... no restart was attempted and the running ComfyUI was left alone." } ``` On an S4U host the kill is always refused. The runtime sits in session 0, the converger runs unelevated in session 1, so `Stop-Process` cannot touch it. The sequence becomes: detach the runtime from its task, discover the port is still held, then throw a message claiming the runtime was left alone. It was not left alone. It was orphaned. That false message is why this reads as a safe failure. ## Why it matters more than the failure it replaced #890 fixed the loud symptom, where a converge left a dead endpoint. What is left is quieter. The endpoint keeps answering, so an operator reads "left alone" and believes nothing changed, while the task sits `Ready` with no instance for `RestartCount=5` to act on and a logon-only trigger. The next time that python exits, nothing recovers it until someone logs in. This is also where the orphaned runtimes in #883 come from. A converge run against a live S4U runtime manufactures one every time and reports success at leaving it alone. ## Measured on kai-tower-3026, 23:30 PDT ``` task \ComfyUI : State=Ready LastTaskResult=0x41306 (SCHED_S_TASK_TERMINATED) LastRunTime=22:27:06 trigger=logon only 127.0.0.1:8188 : LISTEN pid 39132 python sessionId=0 CreationDate : 22:27:06 (task instance a4d64f2b) ParentPid : 42056 cmd.exe, gone since the 22:41:28 stop /system_stats : HTTP 200 ``` Serving normally, supervised by nothing. ## The change Free the port first. A refused kill now leaves a supervised runtime and a throw that is true. `Stop-ScheduledTask` moves below the guard and stays ahead of the start, because `MultipleInstances IgnoreNew` would drop a start while the old instance is alive. Four lines moved, no behavior change on a host where the kill succeeds. ## Verification Parsed clean with `[Parser]::ParseFile` (3215 tokens). PSScriptAnalyzer is not installed on this host. **Not verified live, deliberately.** Exercising this path means running the converger against the live ComfyUI, which is the destructive act at the center of #883. The current runtime is already an orphan and recovering it needs an elevated stop of pid 39132 before `Start-ScheduledTask`, which drops the endpoint for one startup. That wants an operator who knows nothing is mid-render, so I left it for a live-collab pass rather than doing it to prove a four-line reorder.
fix(comfyui): stop the converger orphaning the runtime it cannot kill
All checks were successful
TruffleHog / Scan for secrets (pull_request) Successful in 5s
CI / lint (pull_request) Successful in 44s
2c1fb3a915
The restart guard added in #890 runs Stop-ScheduledTask before it checks
whether the loopback port can be freed. On an S4U host the kill is always
refused, because the runtime sits in session 0 and the converger runs
unelevated in session 1, so the sequence is: detach the runtime from its
task, discover the port is still held, then throw a message saying the
running ComfyUI was left alone.

It was not left alone. It was orphaned. The endpoint keeps answering, so
the operator reads the message and believes nothing changed, while the
task sits Ready with no instance for RestartCount to act on and a
logon-only trigger. Measured on kai-tower-3026 at 23:30 PDT: python 39132
serving 127.0.0.1:8188, created 22:27:06 by task instance a4d64f2b, its
parent cmd.exe gone since the 22:41:28 stop, task Ready with
LastTaskResult 0x41306.

Free the port first. A refused kill now leaves a supervised runtime and a
throw that is true. Stop-ScheduledTask moves below the guard, still ahead
of the start because MultipleInstances IgnoreNew would drop a start while
the old instance lives.

This is independent of the S4U placement question in #888. It is correct
whichever way that decision goes.

Refs: #883

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/infrastructure!892
No description provided.