fix(serve): degrade startup banner to ASCII on non-UTF-8 consoles (#522)#526
Merged
Conversation
`kbagent serve --ui` crashed on startup on Windows with a non-UTF-8 console codepage (e.g. cp1250 on Czech/Polish/Hungarian Windows 11). The startup banner writes box-drawing glyphs (├─ / └─) via sys.stdout.write, which raised UnicodeEncodeError before uvicorn.run was reached -- so the server never bound the port. The same crash hit any legacy single-byte console encoding. Transliterate the glyphs to ASCII (|- / `-) when sys.stdout.encoding cannot represent them, mirroring the UTF-8/ASCII fallback install.sh already uses for its banner. A belt-and-braces try/except around the write re-emits lossily so a purely cosmetic banner can never abort startup. Modern UTF-8 terminals are unaffected -- the glyphs are kept verbatim. The banner build is split into _encode_safe() (pure transliteration decision) and _write_banner() (the I/O wrapper), both unit-tested; an AST-based regression test extracts the live banner literals from serve_command and asserts every glyph they emit has an ASCII fallback, so a future glyph added without extending the table fails the test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #522 —
kbagent serve --uicrashed on startup on Windows with a non-UTF-8console codepage (e.g. cp1250 on Czech/Polish/Hungarian Windows 11).
The startup banner in
commands/serve.pywrites box-drawing glyphs (├─/└─)via
sys.stdout.write. On a legacy single-byte console those glyphs cannot beencoded, so the write raised
UnicodeEncodeErrorbeforeuvicorn.runwasreached — the server never bound the port. Both the
--uiand API-only bannerbranches were affected.
Fix
|-/`-) whensys.stdout.encodingcan't represent them. This mirrors the UTF-8/ASCIIfallback
install.shalready uses for its own banner (Unicode only under a UTF-8 locale, ASCII
otherwise).
try/except UnicodeEncodeErroraround the write re-emitslossily, so a purely cosmetic banner can never abort server startup.
The banner logic is split into two small helpers to match the existing style in
this module (cf.
_default_conversation_id):_encode_safe(text, encoding)— pure transliteration decision, fully unit-testable._write_banner(text)— the I/O wrapper with the fallback.Testing
New
tests/test_serve_banner_encoding.py(19 tests):write()raisesUnicodeEncodeErroronthe glyphs exactly like a real Windows console — with a fidelity test proving
the fake reproduces the crash, so the passing case isn't vacuous.
_encode_safe(UTF-8 keeps glyphs; cp1250/cp1252/latin-1/ascii transliterate;
Noneencoding; unknown-codecLookupError)._write_bannerdoesn't crash on cp1250, preserves Unicode on UTF-8, and thebelt-and-braces path survives a stream that lies about its encoding.
serve_command, extracts the livebanner = (...)literals, and asserts every non-ASCII glyph they emit has anentry in
_BANNER_ASCII_FALLBACK— so a future glyph added to the bannerwithout a fallback fails CI rather than shipping another crash.
All 69 serve-related tests pass;
ruff check,ruff format --check,ty check,and
make changelog-checkare green. No command signature changed, so noplugin/agent-doc sync is required.
Reproduction (before this fix)
The documented workaround (
set PYTHONUTF8=1) is no longer needed.