fix(environments): pass encoding='utf-8' errors='replace' to all text-mode Popen calls - #47951
fix(environments): pass encoding='utf-8' errors='replace' to all text-mode Popen calls#47951liuhao1024 wants to merge 2 commits into
Conversation
…-mode Popen calls On Windows, subprocess.Popen with text=True but no explicit encoding uses the system ANSI code page (e.g. GBK on Chinese Windows). When a child process outputs non-UTF-8 bytes, Python's internal _readerthread crashes with UnicodeDecodeError. Fix by adding encoding='utf-8' and errors='replace' to: - _popen_bash() in tools/environments/base.py (shared by Docker/SSH/Singularity) - _SlashWorker in tui_gateway/server.py - CopilotACPClient._run_prompt in agent/copilot_acp_client.py Fixes NousResearch#47939
|
Related: this fix overlaps with open PR #45099, which already adds |
|
This PR overlaps with #45099, which was created 5 days earlier and covers more files ( One key difference: this PR uses Recommend closing this PR in favor of #45099 for broader coverage, but adopting the |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the Windows text-pipe failure mode. The direct changes to _SlashWorker and CopilotACPClient address real current-main call sites: both construct text=True pipes without an explicit decode policy (tui_gateway/server.py:314-325, agent/copilot_acp_client.py:506-515), and #52649 records a CP936 failure in _SlashWorker._drain_stderr.
Problems
- The stated all-text-mode-Popen scope remains incomplete:
scripts/run_tests_parallel.py:257-270still hastext=Truewithoutencoding/errors. The related open PR #45099 includes that site. - The
_popen_bashchange is not an active output-decoding fix on current main.BaseEnvironmentreads the pipe withos.read()and decodes through UTF-8errors="replace"itself (tools/environments/base.py:644-655; introduced byf336ae3d7). - The added tests only inspect
_popen_bash; they do not cover either direct reader-thread call site changed here.
Suggested changes
- Add focused regression coverage for the TUI and ACP Popen decode policy, and either cover the remaining runner site or narrow the PR's scope.
Automated hermes-sweeper review.
| # Ensure UTF-8 with replacement so non-decodable bytes (e.g. GBK on | ||
| # Chinese Windows) never crash Python's internal _readerthread. | ||
| kwargs.setdefault("encoding", "utf-8") | ||
| kwargs.setdefault("errors", "replace") |
There was a problem hiding this comment.
_popen_bash output is not consumed through this TextIOWrapper on current main: BaseEnvironment._wait_for_process() reads the raw fd and incrementally decodes UTF-8 with errors="replace" (tools/environments/base.py:644-655). This change should not be treated as coverage for the active environment-output path.
| env2 = _TestableEnv() | ||
| assert env1._cwd_marker != env2._cwd_marker | ||
|
|
||
|
|
There was a problem hiding this comment.
These tests only cover the base-helper kwargs, while the direct vulnerable readers changed by this PR are _SlashWorker and CopilotACPClient. Please add focused coverage for those Popen calls (or their undecodable-stderr behavior).
|
Closing as resolved by PR #70875 (merged, commit 0f732cb), which closed out this bug class codebase-wide: every |
What does this PR do?
Adds explicit
encoding="utf-8"anderrors="replace"to allsubprocess.Popencalls that usetext=Truebut were missing these parameters. On Windows,text=Truewithout explicit encoding uses the system ANSI code page (e.g. GBK on Chinese Windows), which causes Python's internal_readerthreadto crash withUnicodeDecodeErrorwhen the child process outputs non-UTF-8 bytes.Related Issue
Fixes #47939
Type of Change
Changes Made
tools/environments/base.py: Addedkwargs.setdefault("encoding", "utf-8")andkwargs.setdefault("errors", "replace")to_popen_bash()— the shared Popen helper used by Docker, SSH, and Singularity backends. Usessetdefaultso callers can still override if needed.tui_gateway/server.py: Addedencoding="utf-8", errors="replace"to_SlashWorker.__init__()Popen call.agent/copilot_acp_client.py: Addedencoding="utf-8", errors="replace"toCopilotACPClient._run_prompt()Popen call.tests/tools/test_base_environment.py: AddedTestPopenBashEncodingDefaultswith 2 tests verifying the defaults and caller override.How to Test
python -m pytest tests/tools/test_base_environment.py::TestPopenBashEncodingDefaults -v— verifies_popen_bashpasses correct encoding paramspython -m pytest tests/tools/test_base_environment.py -v— full base environment test suite (20 tests)UnicodeDecodeErrorChecklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/tools/test_base_environment.py -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/ACode Intelligence
_popen_bash(callers: Docker, SSH, Singularity environments),_SlashWorker.__init__,CopilotACPClient._run_promptLocalEnvironment._run_bashalready hadencoding="utf-8", errors="replace"(line 599-600); this PR applies the same safeguard to the remaining code paths