ComfyUIManagerLogger breaks colorama: isatty() hardcoded to False and missing closed attribute strip ANSI colors from print() on Windows

Author: hwprinzCreated Sep 9, 2026Updated Sep 9, 2026

ANSI colors stripped from print() when file logging is on — ComfyUIManagerLogger reports isatty()==False

Repo: Comfy-Org/ComfyUI-Manager (formerly ltdrdata/ComfyUI-Manager; old address redirects) Version: 3.41 OS: Windows 11 (any Windows where colorama is installed)

Symptom

With enable_file_logging = True, colored print() output loses its ANSI codes from a certain point onward — in both the console and the .log file. By contrast, messages emitted through ComfyUI's own logger (i.e. logging.info/warning/... via the logging module, as ComfyUI core and most nodes do) keep their colors throughout.

The break point is not the start of startup: everything before a specific node's import is colored, everything after is not. That's what makes it look "intermittent" but it's fully deterministic.

Trigger

Any package that calls colorama.init() at import time. The one hitting this today is deepdiff, but the real condition is not a version number — it's the presence of deepdiff/colored_view.py:

  • deepdiff/colored_view.py runs colorama.init() on Windows at import time (when colorama is installed), guarded only by os.name == "nt" and find_spec("colorama").
  • deepdiff/diff.py imports that module at package import, so it fires the moment any node imports deepdiff.

The trigger is therefore "the installed deepdiff ships a colored_view.py that calls colorama.init()". Which versions have that file varies — it is not simply "≥ 9.0". Empirically, on the affected install:

  • deepdiff 8.6.2 → still has colored_view.py → bug present.
  • deepdiff 8.5.0 → no colored_view.py → bug gone.

(The exact version boundary is a deepdiff packaging detail and changes between releases, so it's not worth pinning a "below X" recommendation here.)

deepdiff is not part of the base ComfyUI portable package — a fresh download ships with no deepdiff at all. It only appears because a node installs it: ComfyUI-Crystools declares deepdiff unpinned (requirements.txt is just deepdiff), so on install it pulls whatever the latest release is at that time. On a machine whose install predates the deepdiff release that added colored_view.py, the bug never fires — same node, same OS, different outcome, purely by install date.

So the color strip begins exactly at Crystools' import (where deepdiff is first loaded) and affects every print() after it.

Root cause

ComfyUIManagerLogger (prestartup_script.py) replaces sys.stdout/sys.stderr when file logging is on. As of main (verified 2026-09-09, commit f82970b7, release 3.41 — the line numbers below are stable in current main):

  • def isatty(self): at line 302 hardcodes return False at line 303, and
  • the class (line 286) defines no closed attribute anywhere in the file.

colorama's AnsiToWin32.__init__ probes the stream:

python
have_tty = not self.stream.closed and self.stream.isatty()
...
if strip is None:
    strip = need_conversion or not have_tty

With closed missing (→ colorama's StreamWrapper.closed property catches AttributeError and returns True) and isatty() hardcoded False, have_tty is Falsestrip is forced True unconditionally → colorama wraps sys.stdout in a stripping StreamWrapper.

Every later print() resolves sys.stdout by name → hits the wrapper → ANSI bytes are deleted before the write → color gone in both the console and the file log. logging.* keeps color only because its StreamHandler holds the pre-wrap stream object by reference and bypasses the wrapper.

Note the class already does the correct thing for fileno() — it delegates to original_stdout/original_stderr. The hardcoded isatty() and missing closed are the inconsistent part.

Proposed fix

Replace the hardcoded isatty() and add the missing closed property, in ComfyUIManagerLogger, delegating to the real underlying streams the class already captures (it already does this for fileno() directly above):

diff
--- prestartup_script.py
+++ prestartup_script.py
@@ -300,7 +300,27 @@
                 raise ValueError("The object does not have a fileno method")
 
         def isatty(self):
-            return False
+            # Delegate to the real console we wrap, not a hardcoded False.
+            # Returning False here made colorama (and any ANSI-aware lib) believe
+            # the stream is a pipe and strip every ANSI code from the print() path
+            # in both the console and the file log. The console still receives the
+            # text unchanged -- this only tells colorama the truth about the target.
+            stream = original_stdout if self.is_stdout else original_stderr
+            try:
+                return stream.isatty()
+            except (AttributeError, ValueError, OSError):
+                return False
+
+        @property
+        def closed(self):
+            # Same reasoning as isatty(): without this attribute, colorama's
+            # StreamWrapper.closed falls back to True (AttributeError -> closed),
+            # which again forces strip=True. Report the real underlying state.
+            stream = original_stdout if self.is_stdout else original_stderr
+            try:
+                return stream.closed
+            except (AttributeError, ValueError):
+                return True
 
         def write(self, message):
             global is_start_mode

With honest answers, in a real terminal colorama's should_wrap() is False, so it never wraps and print() keeps its ANSI in both the console and the log. When output is genuinely redirected to a pipe, colorama strips correctly (which is the desired behavior there).

Why it's a Manager bug, not a deepdiff bug

deepdiff's import-time colorama.init() is only the first trigger. The defect is the logger lying about the stream; any ANSI-aware library that consults isatty()/closed before emitting color would hit the same strip. Downgrading deepdiff or pinning Crystools only papers over the symptom.

Verified

Patch applied locally to Manager 3.41 on a Windows 11 / RTX 5090+4060Ti install; all post-Crystools print() lines are colored again in both the console and the fresh user/comfyui_*.log.

Source: Comfy-Org/ComfyUI-Manager