Describe the bug
chainlit lint-translations crashes with UnicodeEncodeError on a Windows console using a legacy code page, because the status markers it prints are non-ASCII. The command is unusable there unless PYTHONIOENCODING=utf-8 is set in the environment first.
To Reproduce
On Windows, from a directory with a .chainlit/translations/ directory, with stdout on a legacy code page (cp1252 is the default for many locales):
import io, sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="cp1252")
from chainlit.cli import cli
cli(["lint-translations"], standalone_mode=False)
Traceback:
File "backend/chainlit/translations.py", line 63, in lint_translation_json
print(f"{error}")
File ".../Lib/encodings/cp1252.py", line 19, in encode
return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u274c' in position 0: character maps to <undefined>
U+274C is the ❌ marker in lint_translation_json. The ✅ marker (U+2705) on the success path has the same problem, so a fully clean run also crashes - it just needs at least one file to lint.
Note the failure is partial and misleading: Linting ar-SA.json... is written before the throw, so the user sees the command begin and then die, with no indication that the cause is console encoding rather than a broken translation.
Expected behavior
The command reports its results on any console. Where the stream cannot represent the markers they should degrade rather than raise.
Environment
- Windows, Python 3.13
- Reproduced against
main (643863b) and confirmed the same code path exists in translations.py
- Works only with
PYTHONIOENCODING=utf-8 exported beforehand
Suggested fix
A dependency-free fallback in translations.py, which currently imports nothing by design:
import sys
def _safe_print(message: str) -> None:
"""Print, degrading when the stream cannot encode the text.
The status markers are non-ASCII and a legacy Windows code page raises
UnicodeEncodeError on them.
"""
try:
print(message)
except UnicodeEncodeError:
encoding = sys.stdout.encoding or "ascii"
print(message.encode(encoding, "replace").decode(encoding, "replace"))
then route the three prints in lint_translation_json through it. That keeps the emoji wherever the console supports them and substitutes replacement characters where it does not, rather than replacing the markers with ASCII everywhere and changing the output for everyone.
An alternative is sys.stdout.reconfigure(errors="replace") in the CLI command, which is a smaller diff but mutates global stream state and does not help callers using lint_translation_json programmatically.
Note on sequencing
I have the fix ready but have deliberately not opened a PR for it: it touches the same lint_translation_json body as #2996, so the two would conflict textually. Happy to send it once #2996 is resolved either way, or immediately if you would prefer it folded into that PR instead.
Describe the bug
chainlit lint-translationscrashes withUnicodeEncodeErroron a Windows console using a legacy code page, because the status markers it prints are non-ASCII. The command is unusable there unlessPYTHONIOENCODING=utf-8is set in the environment first.To Reproduce
On Windows, from a directory with a
.chainlit/translations/directory, with stdout on a legacy code page (cp1252 is the default for many locales):Traceback:
U+274Cis the❌marker inlint_translation_json. The✅marker (U+2705) on the success path has the same problem, so a fully clean run also crashes - it just needs at least one file to lint.Note the failure is partial and misleading:
Linting ar-SA.json...is written before the throw, so the user sees the command begin and then die, with no indication that the cause is console encoding rather than a broken translation.Expected behavior
The command reports its results on any console. Where the stream cannot represent the markers they should degrade rather than raise.
Environment
main(643863b) and confirmed the same code path exists intranslations.pyPYTHONIOENCODING=utf-8exported beforehandSuggested fix
A dependency-free fallback in
translations.py, which currently imports nothing by design:then route the three prints in
lint_translation_jsonthrough it. That keeps the emoji wherever the console supports them and substitutes replacement characters where it does not, rather than replacing the markers with ASCII everywhere and changing the output for everyone.An alternative is
sys.stdout.reconfigure(errors="replace")in the CLI command, which is a smaller diff but mutates global stream state and does not help callers usinglint_translation_jsonprogrammatically.Note on sequencing
I have the fix ready but have deliberately not opened a PR for it: it touches the same
lint_translation_jsonbody as #2996, so the two would conflict textually. Happy to send it once #2996 is resolved either way, or immediately if you would prefer it folded into that PR instead.