[BUG] Korean input characters disappear on iOS mobile SSH
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Summary
When connecting via Termius on iOS and running Claude Code over SSH,
Korean input characters disappear at the moment a consonant and vowel are combined.
- Consonant only: works
- Vowel only: works
- Consonant + vowel (e.g.
ㄱ+ㅏ→가): character disappears
⚠️ In the same Termius environment:
- ✅ Android: works correctly
- ❌ iOS: issue occurs
Other CLI/TUI tools such as Gemini CLI and Codex CLI work correctly even on iOS.
Screen Recording
https://github.com/user-attachments/assets/5d2d9796-7ecd-435e-8eb1-0abf334a9f7c
Environment
- Client
- ❌ iOS + Termius (issue occurs)
- ✅ Android + Termius (works)
- Server
- MacOS (SSH)
- Affected application
- Claude Code
- Not affected
- MacOS Terminal / iTerm2
- Gemini CLI
- Codex CLI
Actual Behavior
- During Korean jamo composition, the input buffer deletes the character and the final composed character is not rendered
Technical Analysis (Important)
iOS Termius input behavior
When typing the Korean character 가 on iOS Termius, the actual byte stream received by the server (logged in raw TTY mode) is:
e3 84 b1 7f ea b0 80
Interpretation:
- e3 84 b1 →
ㄱ(U+3131) - 7f → DEL (backspace)
- ea b0 80 →
가(U+AC00)
This means iOS Termius does not send IME preedit composition to the server.
Instead, it emulates composition using a delete + re-insert sequence:ㄱ → delete → 가.
Claude Code appears to mis-handle this sequence:
- Receives
ㄱ→ inserts into buffer - Receives DEL → deletes
ㄱ - Receives final character
가→ insertion is dropped due to internal input state or cursor calculation
As a result, no character remains on screen.
Why this does not occur on Android
On Android Termius:
- Korean IME composition is completed on the client side
- SSH sends only the final composed UTF-8 characters
This matches the behavior of macOS terminals, so Claude Code never receives the delete + re-insert sequence and works correctly.
Why this does not occur on macOS
- macOS Terminal / iTerm2 complete IME composition locally
- SSH transmits only finalized UTF-8 characters
- Claude Code never encounters the problematic delete + insert sequence
Additional Notes
- Changing Termius backspace transmission from DEL (0x7f) to Ctrl-H (0x08):
- Does not fully resolve the issue in Claude Code
- Can introduce input issues in other terminal applications
Conclusion
- This is not a locale, UTF-8, or font configuration issue
- The issue reproduces only on iOS, while Android and macOS work correctly
- Claude Code’s raw TUI input handling does not correctly process
the delete + re-insert based IME composition sequence used by iOS Termius
- To support iOS mobile SSH environments, Claude Code needs:
- Safe handling of IME composition or delete + insert sequences
- Or an alternative line-based / IME-safe input mode
Impact
- Korean (and likely other CJK) input is effectively unusable in Claude Code on iOS
- Copy/paste is the only practical workaround
What Should Happen?
Expected Behavior
- Korean input should be composed and rendered correctly (
가,나,다, etc.)
Error Messages/Logs
Steps to Reproduce
Steps to Reproduce (100% on iOS)
- Connect to a Linux server via SSH using Termius on iOS
- Run
claude code - Try to input Korean text in the prompt
- Example: input
가
- Type
ㄱ→ briefly appears - Type
ㅏ→ the character disappears
Test for checking text input
$ cat > /tmp/ttylog.py <<'PY'
import sys, tty, termios
fd = sys.stdin.fileno()
if not sys.stdin.isatty():
print("stdin is not a TTY. Run this in an interactive SSH shell.", file=sys.stderr)
sys.exit(1)
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
print("RAW tty logger started. (Press Ctrl-\\ to quit)")
while True:
b = sys.stdin.buffer.read(1)
if not b:
break
sys.stdout.write(b.hex() + " ")
sys.stdout.flush()
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
PY
$ python3 /tmp/ttylog.py
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.0.76
Platform
Anthropic API
Operating System
Other
Terminal/Shell
Other
Additional Information
_No response_
13 Comments
Analysis Report: Why Codex CLI Does Not Experience Korean Input Issues on iOS Termius
Executive Summary
This report analyzes how Codex CLI successfully handles the "delete + re-insert" Korean input sequence from iOS Termius, while Claude Code fails to do so. The analysis is intended to serve as a reference for improving Claude Code's input handling.
---
Problem Context
iOS Termius Korean Input Behavior
When typing Korean character
가(ga) on iOS Termius, the following byte sequence is sent to the server:Interpretation:
ㄱis sent가is sentThis "delete + re-insert" pattern is how iOS Termius implements IME composition over SSH, unlike Android Termius or macOS terminals which complete composition locally.
---
Why Codex CLI Works: Three Key Design Principles
1. Grapheme Cluster-Based Backspace Handling
File:
codex-rs/tui/src/bottom_pane/textarea.rs:454-466, 824-848Why This Matters:
unicode-segmentationcrate'sGraphemeCursorㄱ(3 bytes in UTF-8), it correctly removes the entire character---
2. Dedicated Fast Path for Non-ASCII Characters (IME-Safe Path)
File:
codex-rs/tui/src/bottom_pane/chat_composer.rs:1349-1354, 676-695Why This Matters:
---
3. Byte Offset Cursor with UTF-8 Boundary Validation
File:
codex-rs/tui/src/bottom_pane/textarea.rs:30-37Why This Matters:
---
Processing Flow: iOS Termius Korean Input Sequence
When iOS Termius sends the sequence for
가:| Step | Input | Codex Processing | Buffer State |
|------|-------|------------------|--------------|
| 1 |
ㄱ(U+3131) |!ch.is_ascii()→handle_non_ascii_char()→ insert |ㄱ|| 2 | DEL (0x7f) |
delete_backward(1)→prev_atomic_boundary()→ find grapheme boundary → delete | `(empty) |가| 3 |
(U+AC00) |!ch.is_ascii()→handle_non_ascii_char()→ insert |가` ✓ |Key Insight: Each step operates independently and correctly, resulting in the final composed character being displayed properly.
---
Suspected Differences with Claude Code
| Aspect | Codex CLI | Claude Code (Suspected) |
|--------|-----------|------------------------|
| Backspace unit | Grapheme Cluster | Byte or code point? |
| Non-ASCII handling | Dedicated fast path | Same path as ASCII? |
| Unicode library |
unicode-segmentation| Not used or different approach? || Cursor management | Byte offset + boundary validation | Possible cursor calculation errors? |
---
Key Code References for Claude Code Improvement
Dependencies (Cargo.toml)
Critical Files and Line Numbers
codex-rs/tui/src/bottom_pane/textarea.rsdelete_backward(): lines 454-466prev_atomic_boundary(): lines 824-848next_atomic_boundary(): lines 850-874codex-rs/tui/src/bottom_pane/chat_composer.rshandle_non_ascii_char(): lines 676-695codex-rs/tui/src/bottom_pane/paste_burst.rs---
Recommended Improvements for Claude Code
1. Implement Grapheme Cluster-Based Deletion
2. Separate Non-ASCII Character Path
3. Ensure UTF-8 Boundary Safety
---
Conclusion
Codex CLI successfully handles the iOS Termius Korean input sequence because:
Claude Code likely fails because it lacks one or more of these safeguards, causing the final composed character to be lost during the "delete + re-insert" sequence.
---
Repository Reference
codex-rs/tui/src/bottom_pane/textarea.rscodex-rs/tui/src/bottom_pane/chat_composer.rscodex-rs/tui/src/bottom_pane/paste_burst.rscodex-rs/tui/Cargo.tomlAnalysis Report: Why gemini-cli Does Not Experience Korean Input Issues on iOS Termius
Executive Summary
This report analyzes how gemini-cli handles the "delete + reinsert" sequence sent by iOS Termius during Korean (Hangul) character composition, and why it does not experience the character loss bug that affects Claude Code.
---
Problem Context
iOS Termius Korean Input Behavior
When typing the Korean character
가(ga) on iOS Termius, the following byte sequence is sent to the server:Interpretation:
e3 84 b1→ㄱ(U+3131, initial consonant)7f→ DEL (backspace)ea b0 80→가(U+AC00, composed character)iOS Termius implements Korean composition using a delete + reinsert pattern rather than standard IME preedit sequences.
Why Claude Code Fails
Claude Code appears to mishandle this sequence:
ㄱreceived → inserted into bufferㄱdeleted가received → insertion fails or is dropped due to internal state or cursor calculation issues---
Key Finding: gemini-cli's Dual-Level DEL Handling
gemini-cli handles DEL (0x7f) at two independent levels, ensuring robust handling regardless of how the input arrives.
---
Level 1: Key Event Parsing (KeypressContext.tsx:464-467)
When DEL arrives as an individual key event, it is recognized as
backspace.---
Level 2: Inline DEL Processing in Insert Function (text-buffer.ts:1947-1961)
This is the critical mechanism. The insert function iterates through the input string by code points and immediately dispatches a backspace action when encountering DEL.
---
Dual Safety Net in handleKeypress (text-buffer.ts:2237-2247)
---
iOS Termius Sequence Processing Flow
| Step | Received Data | gemini-cli Action | Result |
|------|--------------|-------------------|--------|
| 1 |
ㄱ(e3 84 b1) | currentText = "ㄱ" | Accumulated in buffer || 2 | DEL (7f) | insert("ㄱ") → backspace() | ㄱ inserted then immediately deleted |
| 3 |
가(ea b0 80) | currentText = "가" | Accumulated in buffer || 4 | Loop ends | insert("가") | Final result: "가" displayed |
---
Why This Approach Works
1. Synchronous Transaction Processing
The
insert-backspace-insertsequence is processed as a single synchronous transaction within the insert function. No rendering or async state updates intervene.2. Code Point-Based Parsing
toCodePoints()correctly splits UTF-8 strings by code points, preventing Korean composed characters from being corrupted.3. Intentional DEL Preservation (textUtils.ts:94-120)
DEL (0x7f) is intentionally preserved because it is handled functionally, unlike other control characters that are stripped.
---
Recommended Fix for Claude Code
Root Cause (Hypothesized)
Claude Code likely processes DEL only as a separate key event and does not handle DEL inline within insert operations. This may cause:
Solution 1: Add Inline DEL Processing to Insert Function
Solution 2: Ensure Code Point-Based String Processing
Solution 3: Preserve DEL in Input Sanitization
---
Reference Files in gemini-cli
| Purpose | File Path | Lines |
|---------|-----------|-------|
| Inline DEL Processing |
packages/cli/src/ui/components/shared/text-buffer.ts| 1947-1961 || Key Event Parsing |
packages/cli/src/ui/contexts/KeypressContext.tsx| 464-467 || handleKeypress Handler |
packages/cli/src/ui/components/shared/text-buffer.ts| 2237-2247 || Code Point Utilities |
packages/cli/src/ui/utils/textUtils.ts| 35-74 || DEL Preservation Logic |
packages/cli/src/ui/utils/textUtils.ts| 94-120 |---
Conclusion
gemini-cli successfully handles the iOS Termius Korean input sequence through:
Implementing similar mechanisms in Claude Code should resolve the Korean input character loss issue on iOS Termius.
Additional reproduction case
Environment:
ko_KR.UTF-8Confirmed:
Same issue on LXC container environment. This affects more users than just macOS servers.
Hoping for a fix soon - Korean input is currently unusable on iOS.
Any updates? @ThariqS
Ipad 에서 Termius 앱을 사용하면 여전히 입력이 정상적으로 처리가 안됨.
Additional reproduction: a-Shell (iOS)
Environment:
Result:
Korean Hangul jamo are displayed decomposed (not composed into syllables).
Example input attempt: "안녕" → displays as "ㅇㅏㄴㄴㅕㅇ"
Same behavior as Termius on iOS. Confirms this is a Claude Code TUI issue, not specific to any particular iOS terminal app.
Correction: a-Shell vs Termius behavior difference
Two distinct failure modes on iOS:
| App | Symptom | Usable? |
|-----|---------|---------|
| Termius | Characters deleted when jamo combine (consonant+vowel → disappears) | No |
| a-Shell | Jamo displayed decomposed (ㅇㅏㄴㄴㅕㅇ instead of 안녕) but input goes through | Barely (readable) |
a-Shell is at least functional for communication, Termius is completely broken for Korean input in Claude Code.
Hi team, this bug makes it very difficult to use the tool on iOS. Is there any ETA on when it might be resolved?
I'm a Korean developer who relies on Claude Code daily via iOS terminal apps (Termius, Blink/Mosh, etc.). Korean characters get completely broken during IME composition — characters disappear or get corrupted
as I type, making it impossible to write anything in Korean.
The only workaround right now is copy-pasting from another app, which kills the workflow entirely. This affects every Korean-speaking user on iOS, and there are quite a few of us who'd love to use Claude Code
on the go.
It would mean the world to us if this could get some attention. Thank you!
Additional reproduction: macOS local terminal (mild) + workaround research
Environment:
Findings:
The bug reproduces not only on iOS SSH but also locally on macOS — though the severity differs:
| Setup | Symptom | Severity |
|-------|---------|----------|
| iPad + Termius SSH | Characters deleted on jamo combination | Severe (unusable) |
| macOS + iTerm2 (local) | Occasional character drop during fast IME composition | Mild |
Workarounds tested — all ineffective:
Root cause hypothesis:
Ink TUI calculates Korean syllables as 1-column wide instead of 2-column (same as other CJK characters). This misaligns the cursor position during IME composition, causing characters to be overwritten or dropped when the composed glyph is committed.
Impact: Affects Korean-speaking users both on iOS and macOS. The iOS experience is completely broken; macOS is usable but unreliable.
Would appreciate any update on the fix timeline. Thank you!
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
Root Cause Analysis (from leaked source code)
Since the source code isn't publicly available, I analyzed the leaked/decompiled Claude Code source to trace this bug. Sources:
Bug Trace
When iOS Termius sends Korean input
가, the byte stream is:ㄱ(e3 84 b1) + DEL(7f) +가(ea b0 80).Step 1 — Tokenizer (
src/ink/termio/tokenize.ts):The tokenizer's
groundstate only transitions on ESC (0x1B).\x7f(DEL) is not treated as a control character boundary, so the entireㄱ\x7f가is emitted as a single text token.Step 2 — parseKeypress (
src/ink/parse-keypress.ts:611):Since
s = 'ㄱ\x7f가'doesn't matchs === '\x7f'(length ≠ 1), it falls through all conditions. Result:key.name = '',key.backspace = false.Step 3 — useTextInput.onInput (
src/hooks/useTextInput.ts:442-465) — BUG HERE:The DEL filtering logic (added for Issue #1853) treats the entire input as "just backspaces" and returns early, completely discarding non-DEL characters (
가) in the same input chunk.Fix Direction
Either:
\x7fas a separate token boundary (like ESC), soㄱ,\x7f,가become three independent key events.return— strip the\x7fbytes and continue processing the remaining characters (insert가).---
Side note: The fact that community members have to reverse-engineer leaked source code to diagnose bugs like this is... not ideal. If you're not going to fix issues in a timely manner, consider open-sourcing the codebase and accepting community contributions. The code is already out there anyway. Letting the community help would be a win-win.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.