fix(proxy): start on consoles that cannot encode the startup banner

show_banner is the first thing run_server does, and the banner is box-drawing
characters. On a console whose encoding cannot represent them, which is the default
on Windows, click.echo raises UnicodeEncodeError and the proxy exits with
"Application startup failed" before it binds a port. Setting PYTHONIOENCODING=utf-8
works around it.

The banner is decorative, so fall back to a plain "LiteLLM" when the stream cannot
encode it rather than taking the server down with it.
This commit is contained in:
Arthi Arumugam 2026-07-27 16:22:10 +05:30
parent 24123269cc
commit ae9aad8160
2 changed files with 38 additions and 0 deletions

View file

@ -15,3 +15,5 @@ def show_banner():
click.echo(f"\n{LITELLM_BANNER}\n")
except ImportError:
print("\n") # noqa: T201
except UnicodeEncodeError:
print("\n LiteLLM\n") # noqa: T201

View file

@ -0,0 +1,36 @@
import io
import os
import sys
from contextlib import redirect_stdout
sys.path.insert(0, os.path.abspath("../../../.."))
from litellm.proxy.common_utils.banner import show_banner
def _cp1252_stdout() -> io.TextIOWrapper:
"""A stdout that cannot encode the banner, like a Windows console on the default code page."""
return io.TextIOWrapper(io.BytesIO(), encoding="cp1252", newline="")
class TestShowBanner:
def test_show_banner_survives_a_stdout_that_cannot_encode_it(self):
"""The banner is decorative; an unencodable console must not stop the proxy from starting."""
stream = _cp1252_stdout()
with redirect_stdout(stream):
show_banner()
stream.flush()
printed = stream.buffer.getvalue().decode("cp1252")
assert "LiteLLM" in printed
def test_show_banner_prints_the_full_banner_when_stdout_can_encode_it(self):
stream = io.TextIOWrapper(io.BytesIO(), encoding="utf-8", newline="")
with redirect_stdout(stream):
show_banner()
stream.flush()
printed = stream.buffer.getvalue().decode("utf-8")
assert "██" in printed