Selecting text in the TUI corrupts multi-byte UTF-8 (Cyrillic) when copied via OSC 52

Open 💬 4 comments Opened Jun 7, 2026 by zentrolink-ivanzhukov

Description

When selecting non-ASCII text (Cyrillic, and likely any multi-byte UTF-8) inside the Claude Code TUI, the copy is routed through OSC 52 and the resulting clipboard content is mojibake. ASCII/Latin characters in the same selection are preserved; only multi-byte characters are corrupted.

The footer hint confirms the OSC 52 path: sent 28 chars via OSC 52 · if paste fails, hold Shift while selecting for native copy.

Steps to reproduce

  1. Run Claude Code in a terminal that supports OSC 52 (here: integrated terminal in VS Code, Remote-SSH).
  2. Have the TUI display some Cyrillic text (e.g. Главный честный вывод вечера).
  3. Select it with a normal mouse drag (TUI captures the selection → OSC 52).
  4. Paste the clipboard anywhere.

Expected

Clipboard contains the original UTF-8 text, e.g. Главный честный вывод вечера.

Actual

Clipboard contains mojibake, e.g. ÐавĐ¾Đ¹ ÑеÑÑĐ½Đ¹ Đ²ÑĐ¾Đ´ Đ²ĐµĐµÑа — the classic pattern of UTF-8 bytes encoded/decoded as a single-byte codepage. This looks like the OSC 52 base64 step is treating the string as latin1/single-byte instead of UTF-8 before base64 encoding.

Workaround (also confirms root cause)

Holding Shift while selecting (Shift+click / Shift+drag) bypasses the TUI's mouse capture and uses the terminal's native copy. The native path produces correct UTF-8. So the corruption is isolated to the TUI's OSC 52 copy path, not the terminal, OS, or locale.

Confirmed NOT the cause

  • Remote locale is fine: LANG=en_US.UTF-8, /etc/default/locale UTF-8, interactive shell UTF-8.
  • Native VS Code terminal copy/paste of the exact same text round-trips correctly (verified by reading the Windows clipboard byte-for-byte).
  • Pasting known-good UTF-8 into the TUI renders correctly — only the OSC 52 copy direction is broken.
  • Not a renderer issue (terminal.integrated.gpuAcceleration: "off" made no difference to this path).

Suspected fix

In the OSC 52 copy implementation, base64-encode the UTF-8 bytes of the selection (e.g. Buffer.from(text, 'utf8').toString('base64')), not a latin1/binary view of the JS string.

Environment

  • Claude Code: 2.1.168
  • Terminal: VS Code integrated terminal, Remote-SSH
  • Client OS: Windows 11
  • Remote OS: Ubuntu 24.04 (noble)
  • Shell: bash

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗