browser_exec: pin the browser-use CLI pipes to UTF-8 - #87181
Conversation
Duplicate of #87162. Both PRs pin browser_exec text-pipe decoding to UTF-8 with replacement handling and add equivalent non-ASCII regression coverage. |
browser_exec: pin the browser-use CLI pipes to UTF-8
|
subprocess.run in browser_exec passes text=True with no encoding, so the CLI's stdout/stderr are decoded with locale.getencoding() — the ANSI code page on Windows (cp1252, cp932, cp936, cp949), never UTF-8. Meanwhile hermes_bootstrap sets PYTHONIOENCODING/PYTHONUTF8 for children, so the browser-use CLI emits UTF-8. The two sides disagree. On the CJK code pages any non-ASCII output raises UnicodeDecodeError immediately; on cp1252 it usually decodes to mojibake and raises on the undefined bytes 0x81/0x8D/0x8F/0x90/0x9D. UnicodeDecodeError is a ValueError, so it is not caught by the OSError handler around the call and escapes browser_exec instead of becoming a tool_error. encoding="utf-8", errors="replace" matches what the uv-install call at the top of this same file already does, and also fixes the input side: input=code is encoded with the same codec, so non-ASCII code no longer risks UnicodeEncodeError. Fixes NousResearch#87152
cfd3e3c to
d64c7fd
Compare
|
Closing as a duplicate of #87162, which landed first and carries the same fix ( @Enough1122 thanks for the review; the substantive points apply equally to #87162, so I'll leave them there rather than split the discussion. |
Fixes #87152
Problem
browser_execintools/browser_use_cli.pyruns the CLI withtext=Trueand noencoding:text=Trueon its own decodes the child's stdout/stderr withlocale.getencoding(). On Windows that is the ANSI code page — cp1252 on US/Western installs, cp932/cp936/cp949 on CJK ones — never UTF-8.The child, meanwhile, is emitting UTF-8:
hermes_bootstrap.pysetsPYTHONUTF8=1andPYTHONIOENCODING=utf-8inos.environon Windows, and the browser-use CLI inherits them. So the two ends of the pipe disagree by construction.What that produces, verified locally:
So a page title, URL, or log line with a non-ASCII character either comes back to the model as mojibake, or blows up. And the blow-up is not contained — the call site only handles
subprocess.TimeoutExpiredandOSError:UnicodeDecodeErroris aValueError, so it escapesbrowser_execentirely rather than being turned into atool_error(...)the agent can recover from.Fix
Pin both ends to UTF-8:
This is exactly what the
uv tool install browser-usecall at the top of this same file already does (line ~353), so it brings the twosubprocess.runcalls in the module into agreement.errors="replace"also means the decode can no longer raise at all, so the surrounding exception handling does not need widening.It fixes the input direction too:
input=codeis encoded with the same codec, so a script containing a non-ASCII selector or string no longer risksUnicodeEncodeErroron cp1252.Tests
Two tests added to
tests/tools/test_browser_use_cli.py::TestBrowserExec:test_pipes_are_pinned_to_utf8— asserts the kwargs actually handed tosubprocess.run. Deterministic on every platform. Onmainit fails withassert None == 'utf-8'.test_non_ascii_output_round_trips— a fake CLI emittingPágina → cafémust survive the pipe. This one only bites on Windows CI, since POSIX locales are already UTF-8.I could only install a subset of the project deps locally, so 8 tests in this file fail in my environment. That set is identical before and after the change (baseline
8 failed, 84 passed→ with this PR8 failed, 86 passed, the two extra passes being the new tests), so full CI is the real check.Not addressed here
The same
text=True-without-encodingpattern appears at 7 other call sites, which I left alone to keep this focused on the filed issue:Happy to fold them in here or open a follow-up, whichever you prefer.