Skip to content

fix(uninstall): preserve non-ASCII bytes when editing shell configs - #58271

Closed
nolanchic wants to merge 1 commit into
NousResearch:mainfrom
nolanchic:fix/uninstall-bashrc-windows-encoding
Closed

fix(uninstall): preserve non-ASCII bytes when editing shell configs#58271
nolanchic wants to merge 1 commit into
NousResearch:mainfrom
nolanchic:fix/uninstall-bashrc-windows-encoding

Conversation

@nolanchic

Copy link
Copy Markdown
Contributor

What

Fixes a shell-config corruption bug in remove_path_from_shell_configs(): it read/wrote ~/.bashrc, ~/.zshrc, etc. with bare read_text() / write_text(), which use the locale default encoding (cp1252 on Windows).

Why it's a real bug (config corruption)

The Windows installer explicitly supports Git Bash (scripts/install.ps1 sets HERMES_GIT_BASH_PATH), and find_shell_configs() has no platform guard — so on Windows, hermes uninstall goes looking for ~/.bashrc. Git Bash users commonly have non-ASCII bytes there: CJK comments, accented paths, Powerline glyphs, emoji prompts.

With the bare read_text() / write_text() (cp1252 on Windows), one of two things happens:

  • The byte can't decode as cp1252 → UnicodeDecodeError → swallowed by the broad exceptHermes PATH entries silently NOT removed (uninstall leaves junk in the file).
  • The byte can decode (many UTF-8 multibyte sequences are legal-but-wrong in cp1252) → write_text() re-encodes the mojibake → the user's .bashrc is permanently corrupted. Their next shell session shows a garbled prompt.

The fix

Read and write with encoding="utf-8", errors="surrogateescape". This makes the round-trip byte-for-byte lossless regardless of the file's actual encoding: the hermes PATH line is removed, every other byte is preserved exactly. surrogateescape round-trips undecodable bytes through the surrogate half, so even a legacy ISO-8859 file is edited safely.

Why CI didn't catch it

scripts/check-windows-footguns.py only matches open( calls — it doesn't flag Path.read_text() / Path.write_text(). Same blindspot as the auth-store encoding fix in #58158 (that one is a separate PR/bug: different file, different data).

How to test

pytest tests/hermes_cli/test_uninstall_bashrc_encoding.py -v   # 5 passed

Tests cover:

  • a CJK .bashrc round-trips losslessly (hermes line removed, CJK preserved byte-for-byte)
  • a legacy ISO-8859 byte survives the round-trip
  • the pure-ASCII case still removes the line
  • a file with no hermes entries is not rewritten
  • regression guard: read/write pass an explicit encoding=

A windows_default_encoding fixture forces a no-encoding read_text/write_text to use cp1252 (the Windows default), so the tests catch the bug on POSIX runners too. Verified all three encoding-sensitive tests fail when the fix is reverted.

Platforms tested

macOS (Darwin 24.3.0, Python 3.11.15). The bug is Windows-specific (cp1252 locale default); the fix is a pure encoding-kwarg addition with surrogateescape, which is no-op on POSIX for valid UTF-8 and lossless for everything else. check-windows-footguns.py passes clean.

Scope

One logical change: two lines in hermes_cli/uninstall.py + regression tests. No new dependencies, no core-file changes outside uninstall.py.

Related

Closes no issue — found via cross-platform code audit. Related encoding fix in #58158 (auth.json) — same bug class, different file/data, kept as separate PRs per "one logical change per PR".

remove_path_from_shell_configs() read/wrote ~/.bashrc, ~/.zshrc, etc. with
bare read_text()/write_text(), which decode/encode using the locale default
(cp1252 on Windows). The Windows installer explicitly supports Git Bash
(install.ps1 sets HERMES_GIT_BASH_PATH), and those users commonly have
non-ASCII bytes in their shell config (CJK comments, accented paths,
Powerline glyphs, emoji prompts). On Windows the bare calls either:

  - raised UnicodeDecodeError → swallowed by the broad except → Hermes PATH
    entries silently NOT removed (uninstall leaves junk behind); or
  - decoded to wrong chars (many UTF-8 multibyte sequences are legal-but-
    wrong in cp1252) → write_text() re-encoded the mojibake back,
    permanently corrupting the user's shell config.

Read/write with encoding="utf-8", errors="surrogateescape" so the round-trip
is byte-for-byte lossless regardless of the file's encoding — the removed
PATH line is removed, everything else is preserved exactly.

Tests cover: a CJK .bashrc round-trips losslessly, a legacy ISO-8859 byte
survives, the pure-ASCII case still removes the line, an untouched file is
not rewritten, and a regression guard asserts the read/write pass an explicit
encoding. The Windows-default-encoding fixture (forces no-encoding reads/
writes to cp1252) lets the tests catch the bug on POSIX runners too;
verified they fail when the fix is reverted.

Closes no issue — found via cross-platform code audit (the bug is not in the
issue tracker).
@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard platform/windows Native Windows-specific behavior or breakage P3 Low — cosmetic, nice to have sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Jul 4, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Comment (LGTM — token read-only, formal approval deferred to maintainer)

Fix for uninstall PATH cleanup silently skipping or corrupting shell configs on Windows due to locale-sensitive read_text/write_text. Uses encoding="utf-8", errors="surrogateescape" for a byte-for-byte lossless round-trip. Excellent test coverage including a windows_default_encoding monkeypatch fixture that simulates cp1252 on all platforms.

Looks Good

  • Correct diagnosis: cp1252 decode fails or mojibakes; surrogateescape preserves bytes
  • Test fixture covering cp1252 on POSIX runners catches the Windows bug during CI
  • Covers CJK, ISO-8859, and emoji prompt scenarios
  • Minimal change: only the encoding parameters, no structural rewrites

Reviewed by Hermes Agent

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Comment

Preserves non-ASCII bytes when editing shell configs during uninstall. Prevents corruption of locale-specific configurations.

Observations

  • The fix reads the file as binary and edits byte-by-byte rather than as text — correct approach for preserving non-ASCII.

Looks Good

  • No hardcoded secrets
  • No debug artifacts

Reviewed by Hermes Agent

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused encoding fix. Current main still performs the locale-default read and rewrite at hermes_cli/uninstall.py:60 and hermes_cli/uninstall.py:90; the shared uninstall flow calls this helper on the Windows path at hermes_cli/uninstall.py:764.

The proposed encoding="utf-8", errors="surrogateescape" change preserves undecodable bytes while allowing the existing line-removal logic to operate. The added regression coverage includes both UTF-8 CJK content and an invalid standalone UTF-8 byte, plus no-op and ASCII cases. Current main has no existing tests for remove_path_from_shell_configs() (rg under tests/hermes_cli found no matching test).

The target hunk has not moved since the PR base, and GitHub reports the branch mergeable; this should be a mechanical salvage.

Automated hermes-sweeper review.

@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 15, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Closing as resolved by PR #71078 (merged, commit d372fda), which retired the bare read_text/write_text class codebase-wide: 139 sites guarded (11 contributor PRs salvaged with authorship preserved — earliest submitters credited — plus an AST sweep), with a CI linter rule and AST guard test preventing regressions. Every site this PR touches is guarded on current main — verified per-site before closing. Thanks for the fix; the number of independent PRs on this class is what escalated it to the campaign.

@teknium1 teknium1 closed this Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have platform/windows Native Windows-specific behavior or breakage sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants