From 2d7543c61f1334bdcfa741776a357c274830de8b Mon Sep 17 00:00:00 2001 From: Alan Chen Date: Sun, 3 May 2026 22:40:34 +0800 Subject: [PATCH] fix(windows): enforce UTF-8 stdout/stderr to prevent UnicodeEncodeError crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Windows, services and terminals default to cp1252 encoding. The CLI uses box-drawing characters (┌│├└─) in banners, doctor output, and status displays. When print() tries to encode these under cp1252, an unhandled UnicodeEncodeError crashes the gateway on startup. This fix adds early UTF-8 enforcement in hermes_cli/__init__.py: - Sets PYTHONUTF8=1 and PYTHONIOENCODING=utf-8 - Re-opens stdout/stderr with UTF-8 encoding if not already UTF-8 Runs at import time so it protects all CLI subcommands. No effect on Unix (gated on sys.platform == "win32"). Backwards-compatible: on systems already using UTF-8, the function is a no-op. Fixes #10956 --- hermes_cli/__init__.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/hermes_cli/__init__.py b/hermes_cli/__init__.py index b3482b1e68..9141ea93e7 100644 --- a/hermes_cli/__init__.py +++ b/hermes_cli/__init__.py @@ -5,11 +5,43 @@ Provides subcommands for: - hermes chat - Interactive chat (same as ./hermes) - hermes gateway - Run gateway in foreground - hermes gateway start - Start gateway service -- hermes gateway stop - Stop gateway service +- hermes gateway stop - Stop gateway service - hermes setup - Interactive setup wizard - hermes status - Show status of all components - hermes cron - Manage cron jobs """ +import os +import sys + __version__ = "0.12.0" __release_date__ = "2026.4.30" + + +def _ensure_utf8(): + """Force UTF-8 stdout/stderr on Windows to prevent UnicodeEncodeError. + + Windows services and terminals default to cp1252, which cannot encode + box-drawing characters used in CLI output. This causes unhandled + UnicodeEncodeError crashes on gateway startup. + """ + if sys.platform != "win32": + return + os.environ.setdefault("PYTHONUTF8", "1") + os.environ.setdefault("PYTHONIOENCODING", "utf-8") + for stream_name in ("stdout", "stderr"): + stream = getattr(sys, stream_name, None) + if stream is None: + continue + try: + if getattr(stream, "encoding", "").lower().replace("-", "") != "utf8": + new_stream = open( + stream.fileno(), "w", encoding="utf-8", + buffering=1, closefd=False, + ) + setattr(sys, stream_name, new_stream) + except (AttributeError, OSError): + pass + + +_ensure_utf8()