fix(cli): force UTF-8 stdout/stderr on Windows to prevent UnicodeEncodeError (#2817)

On Windows, when stdout/stderr are not a UTF-8 TTY (output piped, redirected
to a file, or running under a legacy code page such as cp1252), Rich cannot
encode the banner and box-drawing glyphs, so the CLI aborts with a
UnicodeEncodeError traceback instead of printing. This breaks basic commands
like `specify --help` and `specify version` whenever their output is captured
rather than written to an interactive terminal.

Reconfigure sys.stdout/sys.stderr to UTF-8 with errors="replace" at the
main() entry point on win32 so output degrades gracefully instead of crashing.
The change is a no-op on POSIX, is guarded by try/except so it can never make
stream setup worse, and lives at the CLI entry point only -- importing
specify_cli as a library does not touch global streams.

Verified on Windows 11 (cp1252): `specify --help` piped and `specify version`
redirected to a file both render correctly and exit 0 without setting
PYTHONUTF8 / PYTHONIOENCODING.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Rafael Figuereo
2026-06-03 09:32:14 -04:00
committed by GitHub
parent 6d511acfb9
commit 9e05195d24

View File

@@ -3323,6 +3323,17 @@ def workflow_catalog_remove(
def main():
# On Windows the default stdout/stderr code page (e.g. cp1252) cannot encode
# the Rich banner and box-drawing glyphs, so the CLI crashes with
# UnicodeEncodeError whenever output is not a UTF-8 TTY (piped, redirected to
# a file, or running under a legacy code page). Force UTF-8 with graceful
# replacement so output degrades instead of aborting. No-op on POSIX.
if sys.platform == "win32":
for _stream in (sys.stdout, sys.stderr):
try:
_stream.reconfigure(encoding="utf-8", errors="replace")
except (AttributeError, ValueError, OSError):
pass
app()
if __name__ == "__main__":