fix(browser): safely decode subprocess output with regression coverage - #63512
fix(browser): safely decode subprocess output with regression coverage#63512plcunha wants to merge 4 commits into
Conversation
…8 bytes
The agent-browser subprocess can emit output in the system code page
(e.g. Cp1252 on Windows with accented characters) rather than UTF-8.
_read_command_output_files and the inline reader in _run_browser_command
opened these temp files with a strict encoding='utf-8' decoder, which
raised UnicodeDecodeError ('utf-8' codec can't decode byte 0xc6 ...) and
turned a recoverable browser error into a hard browser_navigate failure.
Fix:
- add _read_output_file_lossy() that reads bytes and decodes with
errors='replace' so content is always returned
- route both the inline reader and _read_command_output_files through it
Test plan:
- pytest tests/tools/test_browser_output_decode_lossy.py -v (4 passed)
- py_compile tools/browser_tool.py
The Chrome fallback path (_run_tmp) still opened its stdout temp file with a strict encoding='utf-8' decoder. Route it through the same _read_output_file_lossy() helper so non-UTF-8 subprocess output on Windows no longer raises.
Duplicate of #47516 (earliest-open canonical, |
The lossy reader correctly opens real subprocess files in binary mode, but existing browser tests (and potentially file-like wrappers) return str from read(). Calling decode() on that text caused two Lightpanda regressions and made CI slice 4 fail. Accept already-decoded str while retaining lossy UTF-8 decoding for bytes. Also strengthen assertions for the replacement character and remove unused test imports.
|
Fixed the CI regression from the first implementation: existing Lightpanda tests use file-like mocks whose read() returns str, while real subprocess temp files return bytes. The helper now handles both without weakening lossy decoding for malformed byte output. I also tightened the replacement-character assertions and removed unused imports. Local verification: 47 focused browser tests passed, ruff passed, py_compile passed, and git diff --check passed. |
|
Thanks — #47516 has the earlier minimal fix at the same three read sites. I updated this PR to make the relationship explicit. The additional value here is the shared best-effort reader plus regression coverage, including the |
|
Thanks for flagging the overlap. I compared this branch with #47516 and the current #47516 was closed without merge, and current
I have now merged current Given that the earlier implementation is no longer open or present in |
|
Thanks for the focused regression coverage. The premise remains valid on current All required CI checks are passing according to Automated hermes-sweeper review. |
|
Closing as duplicate of #47516. The earlier PR already contains the same lossy-decoding fix. |
|
Request to reopen this PR — a fresh Windows Desktop reproduction shows that the underlying bug is still present on current Fresh reproduction
Current How the fix was lostThere is an unfortunate consolidation chain here:
So the current state is: This is not a request for a competing fourth implementation. Reopening #63512 appears to be the cleanest path because it preserves the existing contributor work and already contains the broader tested implementation that prior review requested. Could maintainers please reopen/re-evaluate this PR against current |
Summary
_read_output_file_lossy()to read agent-browser stdout/stderr as bytes and decode malformed UTF-8 witherrors="replace"._run_browser_command,_read_command_output_files, and the Chrome fallback (_run_tmp) through the helper.str, even when opened in binary mode.Why
The agent-browser subprocess can emit output in the system code page (for example Cp1252 on Windows with accented characters) rather than UTF-8. The previous strict
encoding="utf-8"readers raised:That exception turned a recoverable browser error into a hard
browser_navigatefailure instead of surfacing the subprocess output.The first implementation exposed a second compatibility issue in CI: existing Lightpanda tests mock
open().read()with astr. Calling.decode()on that value caused two regressions. The helper now handles both productionbytesand already-decodedstrvalues.Relationship to #47516
#47516 contains the earlier minimal behavioral fix (
errors="replace") at the same three read sites. This PR is a more defensive, tested variant: it centralizes those reads in one helper, preserves compatibility with text-returning file wrappers/mocks, handles missing temp files best-effort, and adds focused regression coverage. Maintainers can choose the canonical implementation; the tests and compatibility case here are intended to be reusable either way.Test plan
pytest tests/tools/test_browser_output_decode_lossy.py -q— 5 passedpytest tests/tools/test_browser_lightpanda.py tests/tools/test_browser_output_decode_lossy.py -q— 47 passedruff check tools/browser_tool.py tests/tools/test_browser_output_decode_lossy.py— passedpython -m py_compile tools/browser_tool.py tests/tools/test_browser_output_decode_lossy.py— passedgit diff --check— passed