Claude CodeのTemp directory拒否で起動しない原因と対処
「Temp directory ... Refusing to use it」で起動直後に終了する原因を、ENOSPC・symlink・uid不一致・権限の4系統で整理し、CLAUDE_CODE_TMPDIRで逃がす手順を示します。
claudeを起動した直後に、次のようなメッセージを出して終了コード1で止まることがあります。
Temp directory /tmp/claude-501 is not a directory (may be an attacker-planted symlink). Refusing to use it. Set CLAUDE_CODE_TMPDIR to a directory you control, or ask an administrator to remove it.Claude Codeが起動時に作る一時ディレクトリが、作れないか安全確認に通らないときの挙動です。メッセージの文言は4種類あり、どれが出たかで直し方が変わります。この記事では、文言ごとの原因、手元で確かめるコマンド、そして最後の逃げ道になるCLAUDE_CODE_TMPDIRの使い方を扱います。
先に結論: 拒否された道を直すか、別の道へ逃がす
直し方は2通りです。拒否された/tmp/claude-<uid>そのものを直す方法と、CLAUDE_CODE_TMPDIRで自分が管理するディレクトリに切り替える方法です。後者はどの文言でも通用します。他人所有のディレクトリのように自分では消せないものがあっても、起動は取り戻せます。
起動時に何が起きているのか
macOSとLinuxでは、起動時にclaude-<uid>という私的な一時ディレクトリを作ります。親はシステムの一時ディレクトリか、CLAUDE_CODE_TMPDIRで指定した場所です。作れないとき、またはその場所に既にあるエントリが安全確認に通らないとき、Claude Codeはセッションを始めずに標準エラーへ失敗を出して終了します。
拒否される4つの文言
ENOSPC: no space left on device, mkdir
ディレクトリを作る場所に空き容量がありません。
is not a directory (may be an attacker-planted symlink)
同じパスにディレクトリでないもの、たとえばシンボリックリンクがあります。
is owned by uid 502, expected 501
自分以外のユーザーが先に作ったディレクトリがあります。
is not readable
ディレクトリのモードが変わったか、途中のパスが検索を拒否しています。
uidは環境で変わります。501はmacOSの最初のユーザーによくある値で、Linuxでは1000前後がよく見られます。メッセージ中の数字は、自分のid -uの結果と見比べる材料になります。
切り分けは4つの質問で足ります
文言を読めば系統は分かりますが、メッセージが流れてしまったときは、現物を見るほうが早いです。「はい」と答えた質問の手当てに進み、すべて「いいえ」なら最後の行へ進みます。
Temp directoryの拒否を切り分ける順番
- QENOSPCと出ていますか?答えがはい答えがはい
空き容量が原因です。一時ディレクトリを置いているボリュームの不要ファイルを消すか、別のボリュームへ
CLAUDE_CODE_TMPDIRで逃がします。 - Q`ls -ld /tmp/claude-$(id -u)`の先頭が`l`で始まりますか?答えがはい答えがはい
シンボリックリンクです。リンク自体を消します(リンク先は消しません)。
- Q所有者が自分以外のユーザー名ですか?答えがはい答えがはい
他のユーザーが先に作ったディレクトリです。管理者に消してもらうか、別の場所へ逃がします。
- Q所有者は自分なのに、モードの所有者欄に`r`か`x`がありませんか?答えがはい答えがはい
モード系です。
chmod 0700で戻すか、消して作り直させます。
それでも直らないとき
拒否された経路には触れず、CLAUDE_CODE_TMPDIRで別のディレクトリを指定して起動し直します。
系統ごとの原因と手当て
ENOSPC: 空き容量が足りない
mkdirが容量不足で失敗している状態です。公式の手当ては、一時ディレクトリを置くボリュームの空きを作ることです。
# 一時ディレクトリのあるボリュームの空きを見る
df -h /tmp
# inodeの枯渇でも作れなくなるので、あわせて見る
df -i /tmpdf -iは公式の説明にない補足です。ENOSPCは容量だけでなくinode(ファイルを管理する枠)の枯渇でも起きるため、空き容量があるのに失敗するときは見ておく価値があります。空きを作れない環境では、別ボリュームに置いたディレクトリへ逃がします(後半の手順)。
symlinkに置き換わっている
/tmp/claude-501のパスに、ディレクトリではなくシンボリックリンクなどが居座っているケースです。メッセージが「attacker-planted」と強い言葉を使うのは、共有の一時領域に誰かが別の場所を指すリンクを置いて、書き込み先をすり替える攻撃を避けるための安全確認だからです。自分でリンクを作った覚えがあるなら、攻撃とは限りません。
公式の指示は「指し示されている先ではなく、名前の挙がったエントリ自体を消す」です。
# 何者か確認する(先頭が l ならシンボリックリンク)
ls -ld /tmp/claude-501
# リンクの行き先を見る
readlink /tmp/claude-501
# リンク自体を消す。末尾に / を付けない
rm -- /tmp/claude-501リンク先のディレクトリを消してしまう事故を避けるため、ls -ldで確かめたとおりの名前を、末尾の/なしでそのまま渡します。
他のユーザーが先に作った(uid不一致)
owned by uid 502, expected 501の形です。共有マシンで別ユーザーが同名のディレクトリを作っていた、または自分のuidが過去と変わった環境で起きます。公式は、このディレクトリを消せるのは管理者か所有しているユーザーだけだと述べています。自力で消せないなら、管理者を待たずにCLAUDE_CODE_TMPDIRで逃がすのが現実的です。
メッセージのexpectedの数字が今のシェルのid -uと一致しているかを、最初に確認します。
読み取れない(mode)
この形だけ、メッセージに自力の復旧手順が書かれています。ディレクトリのモードが変わったか、途中のパス要素が検索(x権限)を拒否している状態です。chmod 0700で戻すか、消して作り直させます。
chmod 0700 /tmp/claude-5010700は所有者だけが読み書きと検索をできる設定です。私的な一時ディレクトリという目的に合った値です。
CLAUDE_CODE_TMPDIRで自分が管理する場所へ逃がす
どの系統でも使える手当てです。公式は、拒否されたパスには触れず、自分が管理するディレクトリを指定して起動し直すよう案内しています。
# 自分だけが使えるディレクトリを先に作る(モード700)
mkdir -p -m 700 "$HOME/.cache/claude-tmp"
# 今回の起動だけ試す
CLAUDE_CODE_TMPDIR="$HOME/.cache/claude-tmp" claudeこの変数を指定すると、実際のディレクトリはその下にclaude-<uid>として作られます(Windowsはclaude)。上の例なら$HOME/.cache/claude-tmp/claude-501です。うまく起動できたら、シェルの設定ファイルかユーザー設定に書いて恒久化します。
{
"env": {
"CLAUDE_CODE_TMPDIR": "/data/cc-tmp"
}
}これは~/.claude/settings.jsonへの書き方の例です。次の点に注意します。
- プロジェクト設定とローカル設定の
envに書いても無視されます。シェル、ユーザー設定、管理設定のいずれかに置きます - 親ディレクトリの自動作成や、書き込めないパスを指定した場合の挙動は、env-varsの説明にありません。親は先に作り、自分が書き込めることを
touchなどで確かめます - 長いパスは避けます。深いパスを指すと、Unixソケットや
SendMessageが壊れた不具合がv2.1.161〜v2.1.162で修正されています
起動時のエラーで止まっている間は、設定ファイルを直すより、まずシェルで一度だけ環境変数を付けて起動できるかを見ると、原因が一つに絞れます。環境変数そのものの仕様、サンドボックス内のBashの$TMPDIRとの関係はCLAUDE_CODE_TMPDIRの解説にまとめています。
起動後に似た名前のエラーが出る場合
一時ディレクトリに絡む失敗は、起動時だけではありません。起動後のBash実行中にも、近い文言が出ます。
| 出る場面 | メッセージの例 | 手当て |
|---|---|---|
| 起動時 | メッセージの例Temp directory ... Refusing to use it | 手当てこの記事の手順 |
| Bash実行中 | メッセージの例task output swap refused (tasks dir moved or linked) | 手当てv2.1.260以降へ更新。プロジェクトの一時ディレクトリを確認し、必要ならCLAUDE_CODE_TMPDIRを新しい場所へ |
| Bash実行中 | メッセージの例Your disk quota is full on the filesystem with Claude Code's temp directory ... (EDQUOT) | 手当て同じボリュームの不要ファイルを消す。別のファイルシステムに逃がす |
task output swap refusedは、Bashの出力ファイルを開くたびに「パスが作った時のファイルを指したままか」を検査し、リンクや移動を見つけたら拒否する仕組みです。起動時の拒否と同じ「すり替え防止」の考え方で、実行中の段階で働きます。
ディスク満杯の診断は、コマンドが0以外の終了コードで出力ゼロのときに出ます。出力が失われた場合は、切り詰められたのではなく失われたものとして扱い、空きを作ってからClaudeにコマンドを再実行させます。
古いバージョンでは空白画面になることがある
claude agentsが、一時ディレクトリが満杯・書き込み不可・他ユーザー所有のときにエラーを出さず空白画面で固まる不具合は、v2.1.280で修正されています。古いバージョンでは、同じ根本原因が別の症状で見えることがあります。バージョンを上げても同じ状態なら、この記事の系統に当てはめて確かめます。
一時ディレクトリの中身そのものが気になるときは、Claude Codeが保存するデータの一覧と、作業ディレクトリに残る一時ファイルのtmpclaude-*-cwdファイルの解説が参考になります。シンボリックリンクが原因になる別の例には、worktree作成がsymlinkで失敗するケースがあります。
手元で確かめる最短手順
最後に、再発時に使える診断を1つのスクリプトにまとめます。macOSとLinuxで動く書き方にしてあります。
d="${CLAUDE_CODE_TMPDIR:-/tmp}/claude-$(id -u)"
echo "uid: $(id -u)"
ls -ld "$d" # 種別・所有者・モードを見る
df -h "$(dirname "$d")" # 置き場所の空き容量
df -i "$(dirname "$d")" # inodeの空き親はシステムの一時ディレクトリです。TMPDIRを設定している環境では、上の:-/tmpをその値に置き換えます。出力の1列目がlならリンク、所有者が自分でなければuid不一致、所有者の欄にrかxが欠けていればmode系(chmod 0700で戻す)、dfの空きが0ならENOSPCです。
まとめ
メッセージは4種類でも、最後の手当ては共通です。拒否されたパスに触れず、自分が管理する短いパスのディレクトリをCLAUDE_CODE_TMPDIRに指定します。消せるのは名前の挙がったエントリ自体で、リンク先ではありません。uid不一致は自力で消せない前提で、最初から逃がす選択肢を持っておくと止まる時間が短くなります。