Comfy-Org / Comfy-Org/ComfyUI

prestartup_script.py: unguarded original_stdout.flush() / original_stderr.flush() in sync_write crashes every prompt on Windows with no attached console (OSError: [Errno 22] Invalid argument)

Open Beginner friendly
#13,554 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
133k
Forks
15.7k
Avg merge
1d 6h
Merged PRs (30d)
155

Description

On Windows, when ComfyUI is launched in a way that leaves sys.__stdout__ pointing at an invalid console handle (common with desktop shortcuts, some launchers, or detached-process setups), every prompt that causes any custom node to call print() crashes with:
OSError: [Errno 22] Invalid argument
The failure is deterministic: it happens the first time any node's code calls print() during execution, because PipeWorker.sync_write() calls original_stdout.flush() unconditionally, and that flush hits EINVAL on the broken console handle. Nothing is cached (the node crashed), so the next prompt re-executes the same code path and crashes identically. In a batch run I had 1,294 / 1,294 prompts fail this way — every single one with the same traceback.
The good news is the fix is trivial: a matching flush() method a few lines below in the same file already handles this correctly with a try/except (OSError, ValueError): pass. The two unguarded flush sites just need the same treatment.
Environment

OS: Windows 11 (ComfyUI desktop install)
ComfyUI: (user to fill in — from the server startup banner)
ComfyUI-Manager: (user to fill in — commit/version shown at startup)
Python: 3.11+ (bundled venv)
Launcher: ComfyUI desktop app shortcut (no attached console, or a console whose handle is rejected by Windows on flush)

Reproduction

Launch ComfyUI on Windows in a way where sys.__stdout__.flush() raises OSError: [Errno 22]. The easiest reliable way I've seen is the ComfyUI desktop install launched from its Start-menu/desktop shortcut.
Run any workflow whose graph includes a custom node that calls print() during execution (in my case, kijai's DownloadAndLoadFlorence2Model, which prints a status line inside loadmodel()).
Every prompt fails with the traceback below.

Full traceback
File "...\ComfyUI\execution.py", line 534, in execute
output_data, output_ui, has_subgraph, has_pending_tasks = await get_output_data(...)
File "...\ComfyUI\execution.py", line 334, in get_output_data
return_values = await _async_map_node_over_list(...)
File "...\ComfyUI\execution.py", line 308, in _async_map_node_over_list
await process_inputs(input_dict, i)
File "...\ComfyUI\execution.py", line 296, in process_inputs
result = f(**inputs)
File "...\custom_nodes\comfyui-florence2\nodes.py", line 188, in loadmodel
print(f"Florence2 using {attention} for attention")
File "...\site-packages\comfyui_manager\prestartup_script.py", line 320, in write
self.sync_write(message)
File "...\site-packages\comfyui_manager\prestartup_script.py", line 341, in sync_write
original_stdout.flush()
File "...\ComfyUI\app\logger.py", line 35, in flush
super().flush()
OSError: [Errno 22] Invalid argument
The Florence2 node is incidental — it's just the first node in the workflow that calls print(). Any custom node that prints would hit this.
Root cause
In prestartup_script.py, PipeWorker.sync_write flushes original_stdout / original_stderr without guarding the call:
python# prestartup_script.py, around lines 337-344 (current main)
if not file_only:
with std_log_lock:
if self.is_stdout:
write_stdout(message)
original_stdout.flush() # <-- raises OSError on broken Windows console
else:
write_stderr(message)
original_stderr.flush()
A sibling branch a few lines above (the tqdm-matching path) has the same issue:
python# same file, around lines 312-316
if '100%' in message:
self.sync_write(message)
else:
write_stderr(message)
original_stderr.flush() # <-- same unguarded flush
Meanwhile the flush() method immediately below sync_write already handles this correctly:
pythondef flush(self):
...
with std_log_lock:
try:
if self.is_stdout:
original_stdout.flush()
else:
original_stderr.flush()
except (OSError, ValueError):
pass
So the intent to tolerate flush failures is already established in this file — it's just not applied at the two sync_write-path sites.
Proposed fix
Apply the same try / except (OSError, ValueError): pass pattern to both unguarded sites. Diff:
diff@@ sync_write, around line 340
if not file_only:
with std_log_lock:
- if self.is_stdout:
- write_stdout(message)
- original_stdout.flush()
- else:
- write_stderr(message)
- original_stderr.flush()
+ try:
+ if self.is_stdout:
+ write_stdout(message)
+ original_stdout.flush()
+ else:
+ write_stderr(message)
+ original_stderr.flush()
+ except (OSError, ValueError):
+ pass

@@ tqdm branch, around line 315
if '100%' in message:
self.sync_write(message)
else:
- write_stderr(message)
- original_stderr.flush()
+ try:
+ write_stderr(message)
+ original_stderr.flush()
+ except (OSError, ValueError):
+ pass
Impact of the fix
A failed flush on a broken console handle just means the line isn't force-drained to that console — content still reaches log_file (which has its own independently guarded flush a few lines up) and the in-memory log/stdout wrappers. No data loss, no behavioral change on healthy stdout, and 100% of these prompt-crash-loops go away on affected Windows installs.
I applied this patch locally and Florence2 (and the rest of my batch, 1,294 prompts) started running correctly immediately.
Happy to open a PR if preferred — just wanted to surface the root cause and the diff for a maintainer to take whichever shape is easiest.

Contributor guide

Open the contributing guide

Research direction

Open prestartup_script.py and inspect PipeWorker.sync_write around the two original_stdout/original_stderr.flush() calls, comparing them with the guarded flush() method below. Reproduce with a Windows detached-console launch and a printing custom node, then verify both flush sites tolerate OSError and ValueError without crashing prompts.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.