WSL 上の Claude Code 文字化けを pinentry-gtk2 で解決した

WSL 上の Claude Code 文字化けを pinentry-gtk2 で解決した

WSL 上で Claude Code 使用時に画面が崩れてしまいました。pinentry のマウストラッキング機能と TUI 描画の競合が原因だったため、GUI 版への切り替えで解決した手順をまとめました。おまけとして、ロケールの設定と、Kiro に Claude Code の拡張機能を導入する手順も紹介しています。
2026.07.16

コーヒーが好きな emi です。

私は Windows 11 上の WSL に Claude Code をインストールして VSCode または Kiro から使っています。
以前の記事で、Claude Code の OTLP 認証トークンを pass に保管する設定をしました。

https://dev.classmethod.jp/articles/claude-code-otlp-token-pass-wsl-grafana-cloud/

その構成を入れてから WSL 上で Claude Code を起動するたびに画面が崩れるようになってしまいました。
※Kiro で作業しています。

wsl-garbled-text-fix_3

5;126;11M5;91;12M... のような文字列が流れ込んできて、Claude Code の TUI が読めなくなる状態です。発生するたびに、以下ブログで記載したように Ctrl+C してパスフレーズを入れ直していました。

https://dev.classmethod.jp/articles/wsl-claude-code-skills-struggle/

原因を切り分けて解決できたので、同じ構成でハマっている方向けに手順をまとめます。

動作環境

項目 バージョン
Windows 10.0.26100
ディストリビューション Ubuntu 22.04.5 LTS(Jammy Jellyfish)
Claude Code 2.1.199
pinentry-gtk2 1.1.1-1build2

pass と GPG エージェント(パスフレーズキャッシュ8時間)、otelHeadersHelper の設定は前回記事で構築済みのものをそのまま使っています。

原因 pinentry-curses のマウストラッキングが TUI と衝突する

前回記事で設定した otelHeadersHelper は、Claude Code 起動時と 29 分ごとに実行するスクリプトです。中で pass show を呼んでおり、pass は GPG でトークンを復号します。GPG エージェントのパスフレーズキャッシュ(8 時間)が切れているタイミングでこれが走ると、復号のために pinentry-curses が起動します。

pinentry-curses は、GPG がパスフレーズを安全に入力させるための専用プログラム「pinentry」のうち、GUI を使わずターミナル画面に文字だけで入力欄を描画するタイプのものを言います。pinentry には GUI 版の pinentry-gtk2 やプレーンテキストの pinentry-tty など複数の実装があり、gpg-agent が設定されているどれかを呼び出します。

pinentry-curses はターミナル上に文字だけで罫線やメニューを描画するための定番ライブラリ ncurses を使って画面を描画しています。Claude Code の TUI も、これに類する技術でターミナル画面を描画しています。

ncurses には「マウストラッキング」という追加機能があり、これを有効にすると、ターミナルはマウスのクリックや移動を検知するたびに、それを示す特殊な文字列(エスケープシーケンス)をプログラムに送るようになります。本来この文字列は ncurses アプリ側が受け取って解釈し、マウスでのメニュー選択などに使うものですが、マウストラッキングが有効な間は、マウスを動かすたびにこの文字列が流れ続ける状態でもあります。

この文字列の書式の一つが SGR マウスレポート形式で、ボタン番号;X座標;Y座標M(例: 5;126;11M)のように、マウス操作の内容をエンコードします。冒頭の画像の 5;126;11M... が、まさにこの形式です。

pinentry-curses はこのマウストラッキングを有効にすることがあり、Claude Code の TUI と同じ端末を奪い合っている状態でこれが起きると、本来 pinentry-curses 向けだったマウス操作の通知が、そのまま Claude Code 側の画面に生の文字列として流れ込んでしまいます。これが、文字化けの正体でした。

対処法 pinentry を GUI 版に切り替える

pinentry-curses が端末を占有すること自体が問題なので、パスフレーズ入力を別ウィンドウの GUI に逃がせば解決できるはずです。WSLg(WSL の GUI サポート)が有効な環境なら、pinentry の GUI 版 pinentry-gtk2 が使えます。

sudo apt install pinentry-gtk2
実行結果例(途中 Y 入力)
emiki@<hostname>:~$ sudo apt install pinentry-gtk2
[sudo] password for emiki:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  libgail-common libgail18 libgtk2.0-0 libgtk2.0-bin libgtk2.0-common
Suggested packages:
  gvfs pinentry-doc
The following NEW packages will be installed:
  libgail-common libgail18 libgtk2.0-0 libgtk2.0-bin libgtk2.0-common pinentry-gtk2
0 upgraded, 6 newly installed, 0 to remove and 207 not upgraded.
Need to get 2,362 kB of archives.
After this operation, 7,253 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 libgtk2.0-common all 2.24.33-2ubuntu2.1 [125 kB]
:
:
Setting up pinentry-gtk2 (1.1.1-1build2) ...
Setting up libgail18:amd64 (2.24.33-2ubuntu2.1) ...
Setting up libgtk2.0-bin (2.24.33-2ubuntu2.1) ...
Setting up libgail-common:amd64 (2.24.33-2ubuntu2.1) ...
:
:
emiki@<hostname>:~$

インストールできたら ~/.gnupg/gpg-agent.confpinentry-program を追記します。

echo "pinentry-program /usr/bin/pinentry-gtk-2" >> ~/.gnupg/gpg-agent.conf
cat ~/.gnupg/gpg-agent.conf
実行結果例
emiki@<hostname>:~$ echo "pinentry-program /usr/bin/pinentry-gtk-2" >> ~/.gnupg/gpg-agent.conf
emiki@<hostname>:~$ cat ~/.gnupg/gpg-agent.conf
default-cache-ttl 28800
max-cache-ttl 28800
pinentry-program /usr/bin/pinentry-gtk-2
emiki@<hostname>:~$

設定を反映するため、稼働中の GPG エージェントを一度落とします。

gpgconf --kill gpg-agent
実行結果例
emiki@<hostname>:~$ gpgconf --kill gpg-agent
emiki@<hostname>:~$ 

キャッシュを確実に切った状態で動作確認したいので、続けてエージェントを再起動してキャッシュをクリアします。

echo RELOADAGENT | gpg-connect-agent
実行結果例
emiki@<hostname>:~$ echo RELOADAGENT | gpg-connect-agent
gpg-connect-agent: no running gpg-agent - starting '/usr/bin/gpg-agent'
gpg-connect-agent: waiting for the agent to come up ... (5s)
gpg-connect-agent: connection to agent established
OK
emiki@<hostname>:~$

キャッシュが切れている状態で、実際に Claude Code を起動して確認します。

claude

起動すると otelHeadersHelper が走り、中で呼ばれる pass show の復号のためにパスフレーズが要求されます。下の画像のように、Claude Code の TUI とは別のウィンドウで GUI ダイアログが出てきました。

wsl-garbled-text-fix_4

パスフレーズを入力して OK を押すと、Claude Code の TUI は崩れることなくそのまま起動しました。マウスを動かしてもエスケープシーケンスが流れ込むことはありません。成功です。

おまけ1 ロケールの設定

調査を始めた当初、先に疑ったのはロケールの設定でした。結論としては冒頭の症状の直接の原因ではなかったのですが、これはこれで参考情報として残しておきます。

ロケールとは、ざっくり言うと、どの言語や地域の作法に合わせて日付・数値・文字を扱うかをまとめた設定です。同じ日付でも en_US なら 07/16/2026ja_JP なら 2026年07月16日 のように表示が変わります。これは OS やアプリがロケールを見て書式を切り替えているからです。

書式だけでなく、ある文字を画面上で何文字分の幅で表示するかという判定にもロケールが使われています。日本語などの全角文字は半角文字の 2 倍の幅で表示されますが、この「文字ごとに何文字分の幅として扱うか」というデータもロケールの一部です。TUI アプリはこの文字幅テーブルをもとに罫線やカーソル位置を計算しているため、データが不正確だと表示がずれてしまいます。

現在のロケール設定は locale コマンドで確認できますが、WSL のデフォルトだと LANGC.UTF-8 になっています。

Linux には通常、en_US.UTF-8ja_JP.UTF-8 のように、文字の分類・並び替え順・日付書式・文字幅テーブルといった詳細なデータを locale-gen でビルドして使うロケールがあります。
一方 C.UTF-8locale-gen を実行しなくても glibc[1]に最初から組み込まれているロケールで、「文字コードとして UTF-8 を正しく扱える」という最低限の保証しかなく、locale-gen でビルドされるロケールが持つ文字幅テーブルなどの詳細データは含んでいません。日本語などの全角文字が本来 2 文字分の幅で表示されるべきところ、C.UTF-8 だとその判定が正しくできず、TUI の罫線やカーソル位置がずれてしまうことがあります。

対処はロケールを生成して既定に設定するだけです。以下のコマンドで /etc/locale.gen の該当行を有効化します。ついでに日本語ロケールも生成しておきました。

sudo sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
実行結果例
emiki@<hostname>:~$ sudo sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
[sudo] password for emiki: 
emiki@<hostname>:~$ 
sudo sed -i 's/^# *ja_JP.UTF-8 UTF-8/ja_JP.UTF-8 UTF-8/' /etc/locale.gen
実行結果例
emiki@<hostname>:~$ sudo sed -i 's/^# *ja_JP.UTF-8 UTF-8/ja_JP.UTF-8 UTF-8/' /etc/locale.gen
emiki@<hostname>:~$ 

続けてロケールデータを生成します。

sudo locale-gen
実行結果例
emiki@<hostname>:~$ sudo locale-gen
Generating locales (this might take a while)...
  en_US.UTF-8... done
  ja_JP.UTF-8... done
Generation complete.
emiki@<hostname>:~$

最後に既定の LANGen_US.UTF-8 に設定します。

sudo update-locale LANG=en_US.UTF-8
実行結果例
emiki@<hostname>:~$ sudo update-locale LANG=en_US.UTF-8
emiki@<hostname>:~$ 

設定を反映するには WSL 自体を再起動する必要があります。Windows 側の PowerShell で以下を実行します。

wsl --shutdown

wsl-garbled-text-fix_1

Kiro で作業していると、wsl --shutdown でターミナルが落ちます。

wsl-garbled-text-fix_2

ターミナルを開き直すと LANG=en_US.UTF-8 が反映された状態で使えるようになります。

再起動後、ロケールが反映されているかを確認します。

locale
実行結果例
emiki@<hostname>:~$ locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
emiki@<hostname>:~$ 
locale -a | grep -iE 'en_US|ja_JP'
実行結果例
emiki@<hostname>:~$ locale -a | grep -iE 'en_US|ja_JP'
en_US.utf8
ja_JP.utf8
emiki@<hostname>:~$

LANG=en_US.UTF-8 になり、locale -a にも en_US.utf8ja_JP.utf8 が並んでいることを確認できました。

おまけ2 Kiro に Claude Code の拡張機能を入れる

ここまでの対処で統合ターミナル上の Claude Code は快適に使えるようになりましたが、そもそもエディタの拡張機能として使えば TUI 描画に依存するトラブル自体を避けられるのでは、と思い立ち Kiro に Claude Code の拡張機能を入れてみました。手順を残しておきます。

まず Kiro の拡張機能マーケットプレイスで「claude code」を検索します。発行元が Anthropic の「Claude Code for VS Code」がヒットします。

wsl-garbled-text-fix_5

インストールを押すと、発行元を信頼するかどうかの確認ダイアログが出ます。Kiro は VS Code のフォークで、拡張機能のレジストリが Microsoft 公式の Marketplace とは限らないため、こうした確認が入ります。

wsl-garbled-text-fix_6

拡張機能の発行元確認

「Trust Publisher & Install」を押す前に、識別子(Identifier)が anthropic.claude-code になっていること、インストール数が 3200 万件を超えていて継続的に更新されていること、公式ドキュメント(code.claude.com/docs/ja/vs-code)に載っている拡張機能と一致することを確認しました。VS Code 系の Marketplace では発行元 ID(publisher部分)はなりすましできない仕組みになっているため、anthropic と完全一致しているかを見るのが有効です。似た名前の非公式拡張機能も並んでいたので、発行元名の確認は必須ですね。

インストールが完了すると、左側のアクティビティバーに Claude Code のアイコンが表示されます。

wsl-garbled-text-fix_7

アイコンをクリックすると、ログイン方法を選ぶ画面が出ます。Claude.ai Subscription、Anthropic Console、Bedrock/Foundry/Vertex から選べます。今回は Claude Enterprise のサブスクリプションを使っているので「Claude.ai Subscription」を選びます。

wsl-garbled-text-fix_8

ブラウザでの認証に進む画面になるので、表示された URL を開きます(自動でブラウザが開かない場合は手動でアクセスします)。

wsl-garbled-text-fix_9

ブラウザ側で Claude Code からの接続を許可する画面が出るので、内容を確認して「承認する」を押します。

wsl-garbled-text-fix_10

セキュリティ保護のため再サインインを求められることがあります。その場合は指示に従って再度サインインします。

wsl-garbled-text-fix_11

認証が完了すると「Claude Code の準備が整いました」という画面になります。このウィンドウは閉じて構いません。

wsl-garbled-text-fix_12

Kiro に戻ると、サイドパネルに Claude Code のチャット画面が表示され、すぐに使い始められる状態になっています。

wsl-garbled-text-fix_13

試しに話しかけてみると、作業ディレクトリを認識したうえで日本語で応答が返ってきました。エディタ内の UI として描画されるので、統合ターミナル特有の文字化けを気にせず使えそうです。

wsl-garbled-text-fix_14

エディタ内で描画される分、マウスイベントの垂れ流しのような端末依存の崩れはそもそも起きにくいはずですが、拡張機能を使っても裏側では同じ Claude Code CLI が動いており、otelHeadersHelper から pass show を呼ぶ構造自体は変わりません。GPG のパスフレーズ入力が必要になる場面は、拡張機能を使っていても起こり得ます。ただ、その場合に実際どう振る舞うかまでは今回検証していません。

おわりに

WSL 上で Claude Code を使っていると発生していた文字化けの正体は、pinentry-curses のマウストラッキングがClaude Code の TUI と端末を奪い合っていたことでした。pinentry-gtk2 に切り替えてパスフレーズ入力を別ウィンドウに逃がすことで解決できました。同じ現象で困っている方の参考になれば幸いです。

また、今回のトラブルシューティングは Claude Code の TUI で行いました。自分の端末で設定がどうなっているかコマンドで確認したり、ログを見たりしながら解決できました。とても便利ですね。

脚注
  1. GNU C Library。Ubuntu など多くの Linux ディストリビューションが土台として使っている標準 C ライブラリで、プログラムがファイル操作やメモリ管理、ロケール処理といった OS の基本機能を呼び出す際の窓口になっています ↩︎


Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

この記事をシェアする

AI白書

関連記事