Claude Media
CLAUDE_CODE_NONBLOCKING_STDOUTでターミナル出力の凍結を防ぐ

CLAUDE_CODE_NONBLOCKING_STDOUTでターミナル出力の凍結を防ぐ

一時停止したtmuxやSSH接続でセッションが固まる問題を、非ブロッキングstdout書き込みで回避する環境変数の使い方をまとめます。

このTipsでできること

CLAUDE_CODE_NONBLOCKING_STDOUTは、ターミナルへの出力を非ブロッキングな経路で書き込み、出力側の詰まりによるセッションの凍結を防ぐClaude Codeの環境変数です。1に設定すると、ターミナル出力を2つ目の非ブロッキングなファイルディスクリプタ経由で書き込みます。

対象は、一時停止したtmuxのコントロールモードのペインや、応答が止まったSSH接続のように、ターミナル側が出力を読まなくなる状況です。この状態でもClaude Codeのセッションが途中で固まらなくなります。

macOS・Linux・WSLでstdoutが端末に接続されているときに働き、Claude Code v2.1.261以降が必要です。

やり方

シェルで一度設定するか、settings.jsonのenvキーに恒久的に書いておきます。

export CLAUDE_CODE_NONBLOCKING_STDOUT=1
claude

毎回のセッションで有効にしたい場合は、~/.bashrcや~/.zshrcにこのexport行を追記します。シェルの環境変数はClaude Codeの起動時に読み込まれます。値を変えても、次にclaudeを起動し直すまでは反映されません。

起動前に値を確かめるには、同じシェルで次を実行します。

echo $CLAUDE_CODE_NONBLOCKING_STDOUT

チームやプロジェクト単位で常時有効にしたいときは、settings.jsonのenvキーに書きます。

{
  "env": {
    "CLAUDE_CODE_NONBLOCKING_STDOUT": "1"
  }
}

settings.jsonのenvの値は、起動方法を問わずClaude Codeがファイルから直接読みます。書く場所で適用範囲が変わります。

ファイル適用範囲
~/.claude/settings.json適用範囲自分だけ、全プロジェクト共通
.claude/settings.json適用範囲プロジェクトの全員、リポジトリにコミット
.claude/settings.local.json適用範囲自分だけ、このプロジェクトのみ(gitignore対象)
マネージド設定適用範囲組織の全員、管理者が配布

個人のリモート作業環境全般なら~/.claude/settings.json、tmuxやSSH運用が前提のチームなら.claude/settings.jsonが候補です。マネージド設定に書けば、個々人の設定を待たずに全員へ行き渡ります。

値の書き方と反映タイミング

オン・オフを切り替える変数は、1・true・yes・onで有効、0・false・no・offで無効です。大文字小文字は区別されません。

値の有無だけを見る変数(DISABLE_TELEMETRYなど)は例外として個別に挙がっています。CLAUDE_CODE_NONBLOCKING_STDOUTはその一覧に入っていません。この変数の行には1としか書かれていませんが、オン・オフ変数の一般規則に従えば、trueでもTRUEでも同じ扱いです。

envキーの値は、ファイルを保存した時点で実行中のセッションにも反映されるのが原則です。ただしOpenTelemetryの監視のように、起動時に一度だけ読む機能は再起動まで古い値のままです。この変数がどちらに当たるかは説明に出てこないので、設定後はセッションを再起動しておくと確実です。

逆向きの注意もあります。envキーから変数を消しても、実行中のセッションでは設定が外れません。消した効果は、次にclaudeを起動したときに現れます。オフにしたい場合は、削除ではなく値を0にして再起動すると意図が明確です。

固まるときは何が起きているのか

プログラムがターミナルへ書くとき、標準出力(stdout)への書き込みは、受け取る側が読み出す前提で進みます。受け取る側が止まると、バッファが埋まった時点で書き込みがそこで待たされ、後続の処理が進まなくなります。

次の2つは、どちらも受け取る側が読み出しを止める状況です。環境変数の説明が挙げているのも、この2つです。

状況ターミナル側の状態
tmuxのコントロールモード(iTerm2連携など)のペインが一時停止したターミナル側の状態ペインが出力を読まなくなる
SSH接続が応答しなくなった(ネットワーク切断・スリープ復帰待ちなど)ターミナル側の状態接続の先が出力を受け取らなくなる

この仕組みは、OSの機能だけで再現できます。次のスクリプトは、誰も読まないパイプに非ブロッキングで書き続けます。出力例はmacOSのPython 3.12のものです。

python3 - <<'PY'
import os, fcntl, errno
r, w = os.pipe()
fl = fcntl.fcntl(w, fcntl.F_GETFL)
fcntl.fcntl(w, fcntl.F_SETFL, fl | os.O_NONBLOCK)
n = 0
try:
    while True:
        n += os.write(w, b"x" * 1024)
except OSError as e:
    print("written before full:", n, "bytes ->", errno.errorcode[e.errno])
PY
written before full: 65536 bytes -> EAGAIN

読み手がいないパイプは、この出力例では64KBで満杯になっています。OSやバッファ設定で値は変わります。EAGAINは「いまは書けない」を表すエラーです。

くらべる

書き込みが詰まったときの違い

ブロッキング

通常の書き込み

受け取る側が止まると、バッファが埋まった時点でwriteが戻りません。描画も入力処理も、その呼び出しの後ろで待たされます。

非ブロッキング

この環境変数が有効

2つ目の非ブロッキングなファイルディスクリプタに書きます。書けない状況でも呼び出しは戻るので、セッションの本体が止まりません。

上のスクリプトは一般的なOSの挙動の再現で、Claude Codeの内部実装そのものではありません。環境変数の説明が述べるのは「2つ目の非ブロッキングなファイルディスクリプタ経由で書く」までです。あふれた出力をどう扱うかは、その説明の範囲外です。

固まる原因ごとの切り分け

Claude Codeでセッションが動かなくなる不具合は、原因ごとに個別に直されてきました。症状が似ていても、直る箇所は別です。

あゆみ

「固まる」系の修正の流れ

  1. v2.1.15(2026年1月21日)MCP stdioサーバーのタイムアウト

    タイムアウトしたMCP stdioサーバーの子プロセスが終了せず、UIが固まることがある問題の修正です。

  2. v2.1.71(2026年3月7日)stdin側の固まり

    長時間セッションで、キー入力が処理されなくなるのにプロセスは生きている状態の修正です。

  3. v2.1.73(2026年3月11日)権限プロンプトとskill読み込み

    複雑なbashコマンドの権限プロンプトで起きる固まりと100% CPUのループ、大量のskillファイル変更時のデッドロックの修正です。

  4. v2.1.261(2026年9月4日リリース)stdout側の環境変数の必要版

    環境変数の説明が、CLAUDE_CODE_NONBLOCKING_STDOUTの必要版として挙げるバージョンです。

症状を見て、次の順に当てはめます。

手順

固まったときの当てはめ方

  1. 1

    キー入力が反応しないか

    入力側(stdin)の症状です。この環境変数は対象外で、上のv2.1.71の項目が該当します。長時間セッションで、プロセスは生きているのに入力だけが届かないかを見ます。速い入力やキーリピートで文字が前後したり抜けたりする症状も、v2.1.261の修正項目に別枠で入っています。

  2. 2

    画面が止まりCPUが高止まりしているか

    権限プロンプトやskill読み込みが絡む症状です。これも対象外で、上のv2.1.73の項目が該当します。複雑なbashコマンドの権限プロンプトや、大量のskillファイル変更の直後かを見ます。

  3. 3

    tmuxの一時停止やSSHの無応答の直後に固まったか

    出力(stdout)側の詰まりです。この環境変数の対象で、ターミナル側の状態変化と固まった時刻が一致するかを見ます。

The response stopped arriving(v2.1.227より前はResponse stalled mid-stream)のようなメッセージが出ている場合は、API応答側の停止です。errorsページがサーバー側のエラーとして扱う項目で、端末が読まない話とは別系統です。

設定する前にclaude --versionで、v2.1.261以降であることを確かめてください。それより前のバージョンでは変数が働きません。

tmuxのコントロールモードではフルスクリーン描画も別問題になる

iTerm2のtmux連携は、tmux -CCで入るコントロールモードです。fullscreenページによると、このモードではフルスクリーン描画が使えません。

代替画面バッファとマウス追跡が正しく動かず、ホイールが無反応になったり、ダブルクリックで端末の状態が壊れたりするためです。起動時の描画方式も、このモードでは従来の描画(Classic)が選ばれます。

つまりtmux -CCで作業するときは、2つの設定が別々の軸になります。

  • CLAUDE_CODE_NONBLOCKING_STDOUT=1は、ペインが止まったときに出力側で固まらないための設定です
  • フルスクリーン描画は、このモードでは有効にしない側の設定です

-CCなしの通常のtmuxでは、フルスクリーン描画も使えます。マウスホイールのスクロールにはtmuxのマウスモード(set -g mouse on)が要ります。

SSHでも、描画方式が自動で変わる場合があります。fullscreenページの既定値の表では、Windows上で動くClaude CodeにSSHで接続しているときも、従来の描画(Classic)が選ばれます。

tmux -CCやSSHの先で画面が崩れたときは、まず描画方式が従来のものになっているかを見ます。固まる症状が出る場合に限って、出力経路を変えるこの環境変数を足す、という順番です。

効きやすい環境と効かない環境

効果を体感できるのは、実際にtmuxの一時停止やSSHの応答停止が起きたときだけです。

効きやすいのは、次のような運用です。

  • リモートの開発コンテナやクラウドVMにSSHでつなぎ、Claude Codeを長時間走らせる
  • tmuxでセッションを多重化し、バックグラウンドタスクを監視しながら作業する
  • ノートPCがスリープに入り、SSH接続が一時的に切れる

どれも「ターミナル側の受け取りが止まる瞬間」が日常的に起きます。逆に、ローカルのターミナルを開いたまま使うだけなら、この経路が発生しにくく恩恵は限られます。

対象外になる条件もあります。

  • stdoutが端末に接続されていない場合(パイプやリダイレクトで別プロセスやファイルに流している場合)
  • ネイティブWindows(対応はmacOS・Linux・WSLのみ)

ネイティブWindowsでstdoutが取得できない別の不具合は、Claude CodeがWindowsでstdoutを取得できない原因と対処法が扱っています。原因も対象OSも異なります。

名前の近い変数との取り違え

MCP_CONNECTION_NONBLOCKINGは別の変数です。MCPサーバーの接続を待ってから最初のクエリに進むかを決める設定で、既定では接続をバックグラウンドで進め、0にすると接続完了を待ちます。ターミナル出力の固まりとは関係ありません。

設定しても解決しない場合

CLAUDE_CODE_NONBLOCKING_STDOUT=1でも同じ症状が続くなら、原因が別にあります。固まった瞬間の内部ログを別ファイルに残しておくと、tmuxやSSH側の状態変化とタイムスタンプを突き合わせられます。やり方は--debugと--debug-fileでログ出力先を切り替えるにあります。

同じ手順で使える他の環境変数

設定手順(シェルのexportかsettings.jsonのenvキー)は、ほかの環境変数でも同じです。複数セッションでタスクリストを共有するCLAUDE_CODE_TASK_LIST_IDが一例です。

フルスクリーン描画でのクリック・ドラッグ・ホバー処理を止めるのはCLAUDE_CODE_DISABLE_MOUSE_CLICKSです。Windows Terminalでリンクが2回開く場合の原因と対処はこちらにまとめています。

まとめ

SSHやtmuxを挟んで長時間セッションを回す環境では、~/.claude/settings.jsonにこの変数を1で入れておく価値があります。入力が反応しない、CPUが高止まりするといった症状は別原因なので、「固まったときの当てはめ方」の3つの問いで切り分けてから--debug-fileのログで調べます。

この記事を共有:XはてブLinkedIn