Skip to content

lint-translations crashes with UnicodeEncodeError on a legacy Windows console code page #2997

Description

@Zuhef

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendPertains to the Python backend.bugSomething isn't working

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions