fix(desktop): agent shells inherit a UTF-8 locale on macOS - #6236
Conversation
GUI-launched apps inherit no LANG/LC_* from launchd, so spawned agent CLIs ran in the C locale and pbcopy decoded their UTF-8 output as MacRoman, corrupting any non-ASCII text copied to the clipboard. Harvest LANG/LC_ALL/LC_CTYPE from the login shell alongside the existing PATH and SSH_AUTH_SOCK, and fall back to C.UTF-8 on macOS when neither the process nor the login shell provides one.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 36fe57b. Configure here.
ApprovabilityVerdict: Approved dee3e06 This is a targeted macOS bug fix that adds UTF-8 locale fallback handling for GUI-launched shells. The change is conservative (only applies when no locale is already set), platform-specific, and well-tested with 5 new test cases. You can customize Macroscope's approvability policy. Learn more. |
## What's Changed * fix(desktop): app zoom no longer zooms the preview browser by @juliusmarminge in pingdotgg/t3code#6649 * fix(server): keep provider notification consumers alive past startSession by @tsouth89 in pingdotgg/t3code#6538 * fix(server): treat removed Bitbucket permissions endpoint as unknown, not blocking by @lnieuwenhuis in pingdotgg/t3code#6525 * fix(ssh): let cold remote servers finish starting by @gbarros-dev in pingdotgg/t3code#6168 * fix(web): preserve Claude insight line breaks by @nateEc in pingdotgg/t3code#4344 * feat(web): accept file drops across the chat workspace by @dbalders in pingdotgg/t3code#6636 * fix(web): widen ordered-list marker gutter for 3+ digit item numbers by @lnieuwenhuis in pingdotgg/t3code#6527 * fix(server): bound thread activity hydration by @t3-code[bot] in pingdotgg/t3code#6153 * fix(web): restore the Archive action in the default sidebar thread menu by @lnieuwenhuis in pingdotgg/t3code#6526 * fix(web): open diff files from nested projects by @gbarros-dev in pingdotgg/t3code#6174 * fix(mobile): use tryOpenExternalUrl for markdown links in ThreadFeed by @mohamedmastouri-hue in pingdotgg/t3code#5872 * fix(web): open the file a bare filename reference names by @Brechard in pingdotgg/t3code#6297 * fix(server): stop the provider title mirror from overwriting real thread titles by @repparw in pingdotgg/t3code#5941 * fix(shared): match source-control providers by DNS label by @gbarros-dev in pingdotgg/t3code#6175 * feat(desktop): Chrome-style hold-to-quit by @Bil0000 in pingdotgg/t3code#5508 * fix(gitlab): submit review comments on context lines by @tarik02 in pingdotgg/t3code#6348 * fix(marketing): keep Grok mark clear of mobile hero copy by @NicL9923 in pingdotgg/t3code#4542 * fix(mobile): recover the QR pairing scanner when camera access is denied by @hey-jj in pingdotgg/t3code#6487 * fix(web): keep a long path from running under the folder picker button by @Sy-D in pingdotgg/t3code#4823 * fix(terminal): right-click paste works in the terminal by @StiensWout in pingdotgg/t3code#5240 * fix(mobile): explain iOS-only settings on Android by @danvernon in pingdotgg/t3code#4981 * fix(web): stop counting a workflow coordinator as a working agent by @Rishet11 in pingdotgg/t3code#6672 * fix(web): keep floating preview anchored after panel closes by @AksharP5 in pingdotgg/t3code#6547 * fix(web): unstick /connect after in-modal sign-in by redirecting to the authorize endpoint by @TorbenWetter in pingdotgg/t3code#5133 * fix(web): keep send reachable while a turn is running on mobile by @AMohamedAakhil in pingdotgg/t3code#4781 * fix(web): reject unsupported composer image types at attach time by @mdshzb04 in pingdotgg/t3code#6574 * Make ClaudeTextGeneration tests hermetic on Windows by @mihneaptu in pingdotgg/t3code#4508 * fix(web): show command output in work log by @LikoKiko in pingdotgg/t3code#4083 * fix(web): reserve sibling column width when resizing the right panel by @Lasdw6 in pingdotgg/t3code#6279 * fix(web): replace whitespace in new ref names with dashes by @jorj-pineda in pingdotgg/t3code#6270 * fix(client-runtime): branch list no longer resets while paging through refs by @a20hek in pingdotgg/t3code#5858 * fix(web): support Shift+Insert terminal paste by @aoright in pingdotgg/t3code#5982 * fix(web): keep the composer glass aligned with the context strip at any interface font size by @Williawar in pingdotgg/t3code#5703 * fix(codex): keep background memory out of chats by @AksharP5 in pingdotgg/t3code#5468 * fix(server): treat a missing Codex rollout as a recoverable resume error by @Rishet11 in pingdotgg/t3code#6671 * fix(web): hide provider Update toast action while an update is running by @mrmg in pingdotgg/t3code#6544 * fix(desktop): agent shells inherit a UTF-8 locale on macOS by @Linus-Boehm in pingdotgg/t3code#6236 * fix(server): ignore Claude command lifecycle messages by @naveed949 in pingdotgg/t3code#6606 * docs: mention Bitbucket user read scope needed by auth probe by @thamrx in pingdotgg/t3code#6291 * fix(server): return valid preview action results by @duncan-vc in pingdotgg/t3code#5966 * fix(claude): make "Always allow for session" stick, and only for the session by @kakismash in pingdotgg/t3code#5041 * fix(ssh): surface a failed remote t3 install instead of a silent 0-byte server.log by @TorbenWetter in pingdotgg/t3code#5132 * perf(server): persist the wire projection for streaming tool.updated data by @mInrOz in pingdotgg/t3code#6675 * fix(web): stop wrapping partial code block selections in markdown fences by @JoeJoeflyn in pingdotgg/t3code#5069 * fix(web): hide T3 Connect toggle in web app settings by @JoeJoeflyn in pingdotgg/t3code#5068 * fix(web): show provider account accent badge in sidebar rows and hover card by @vitalyiegorov in pingdotgg/t3code#5980 * fix(server): wait for concurrent SQLite writers instead of failing with SQLITE_BUSY by @ostapondo in pingdotgg/t3code#5134 * fix(web): reject oversized prompts before provider turn start by @naveed949 in pingdotgg/t3code#6602 * feat(web): collapse the question prompt from its header by @Jardo-51 in pingdotgg/t3code#6773 * fix(shared): degrade an unknown system time zone to UTC in usage windows by @Rishet11 in pingdotgg/t3code#6670 * fix(claude): discover repo-local .agents/skills in skill discovery by @RoshanMhatre in pingdotgg/t3code#5488 * fix(server): let slow provider CLIs raise their discovery probe budget by @CDVolvik in pingdotgg/t3code#6223 * fix(web): retain terminal PR badges after checkout switch by @sebbonit in pingdotgg/t3code#4755 * fix(web): show selected model in context window tooltip by @nqrwhal in pingdotgg/t3code#4772 * fix(web): scale command details with code font by @Serendeep in pingdotgg/t3code#6510 * fix(web): preserve XML-like tags in user messages by @0utsights in pingdotgg/t3code#4133 ## New Contributors * @mohamedmastouri-hue made their first contribution in pingdotgg/t3code#5872 * @NicL9923 made their first contribution in pingdotgg/t3code#4542 * @hey-jj made their first contribution in pingdotgg/t3code#6487 * @danvernon made their first contribution in pingdotgg/t3code#4981 * @Rishet11 made their first contribution in pingdotgg/t3code#6672 * @AksharP5 made their first contribution in pingdotgg/t3code#6547 * @TorbenWetter made their first contribution in pingdotgg/t3code#5133 * @AMohamedAakhil made their first contribution in pingdotgg/t3code#4781 * @mdshzb04 made their first contribution in pingdotgg/t3code#6574 * @mihneaptu made their first contribution in pingdotgg/t3code#4508 * @LikoKiko made their first contribution in pingdotgg/t3code#4083 * @Lasdw6 made their first contribution in pingdotgg/t3code#6279 * @jorj-pineda made their first contribution in pingdotgg/t3code#6270 * @a20hek made their first contribution in pingdotgg/t3code#5858 * @aoright made their first contribution in pingdotgg/t3code#5982 * @Williawar made their first contribution in pingdotgg/t3code#5703 * @mrmg made their first contribution in pingdotgg/t3code#6544 * @Linus-Boehm made their first contribution in pingdotgg/t3code#6236 * @naveed949 made their first contribution in pingdotgg/t3code#6606 * @thamrx made their first contribution in pingdotgg/t3code#6291 * @duncan-vc made their first contribution in pingdotgg/t3code#5966 * @kakismash made their first contribution in pingdotgg/t3code#5041 * @mInrOz made their first contribution in pingdotgg/t3code#6675 * @JoeJoeflyn made their first contribution in pingdotgg/t3code#5069 * @vitalyiegorov made their first contribution in pingdotgg/t3code#5980 * @ostapondo made their first contribution in pingdotgg/t3code#5134 * @Jardo-51 made their first contribution in pingdotgg/t3code#6773 * @RoshanMhatre made their first contribution in pingdotgg/t3code#5488 * @CDVolvik made their first contribution in pingdotgg/t3code#6223 * @sebbonit made their first contribution in pingdotgg/t3code#4755 * @nqrwhal made their first contribution in pingdotgg/t3code#4772 * @Serendeep made their first contribution in pingdotgg/t3code#6510 * @0utsights made their first contribution in pingdotgg/t3code#4133 **Full Changelog**: pingdotgg/t3code@v0.0.34-nightly.20260815.1100...v0.0.34-nightly.20260815.1101 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.34-nightly.20260815.1101

Fixes #6231.
GUI-launched macOS apps inherit no
LANG/LC_*from launchd, so agent CLIs spawned by the desktop app ran in theClocale.pbcopydecodes stdin using the locale encoding rather than passing bytes through, so any non-ASCII text an agent copied was silently corrupted —Grüße – ßarrived asGr√º√üe ‚Äì √ü.DesktopShellEnvironmentalready resolves the login shell environment intoprocess.env, which the backend and its agent CLIs inherit. Its allowlist just didn't include the locale. This addsLANG/LC_ALL/LC_CTYPEto the harvest and the existing copy-if-absent pass, then falls back toC.UTF-8on macOS when neither the process nor the login shell supplies one.C.UTF-8rather than the user's full locale on purpose: it fixes the codeset without shifting collation or formatting, so anything parsing tool output is unaffected (sortgivesa b äunderC.UTF-8buta ä bunderde_DE.UTF-8). It's also what macOS's own/etc/zprofilefalls back to —env -i HOME=$HOME /bin/zsh -ilc 'echo $LANG'returnsC.UTF-8.LC_ALLis harvested but never force-set, since it would override everyLC_*category.Scoped to the desktop spawn path. A standalone server started without a locale would have the same problem, but that's a separate concern and I've left it out to keep this small.
Verification: 4 new tests in
DesktopShellEnvironment.test.ts(harvest, inherited-value precedence, macOS fallback, no-fallback-on-linux); the two behavioural ones fail without the source change.vp testgreen for@t3tools/desktop(448 tests), typecheck andfmt --checkclean.No UI change, so no screenshots.
Written with Claude Opus 5, via the Claude Code CLI running inside T3 Code.
Note
Fix agent shells to inherit a UTF-8 locale on macOS
LANG,LC_ALL, andLC_CTYPEto the login shell environment whitelist in DesktopShellEnvironment.ts so they can be hydrated from the login shell.LC_CTYPEfalls back toen_US.UTF-8.LC_CTYPE=en_US.UTF-8set.Macroscope summarized dee3e06.
Note
Low Risk
Scoped macOS-only environment hydration with careful precedence and a UTF-8 LC_CTYPE fallback; no auth or data-path changes.
Overview
Fixes GUI-launched macOS agent shells landing in the
Clocale, which causedpbcopyto corrupt non-ASCII text.DesktopShellEnvironmentnow harvestsLANG/LC_ALL/LC_CTYPEfrom the login shell and hydrates them as a group only when the process has none. If still unset on macOS, it falls back toLC_CTYPE=en_US.UTF-8(leavingLANGunset to keep C-stable collation/formatting). Inherited locales are preserved and not mixed with login-shell categories; Linux is unchanged.Reviewed by Cursor Bugbot for commit dee3e06. Bugbot is set up for automated code reviews on this repo. Configure here.