Claude Code File does not existが画像とPDFで出る原因と対処
ファイルは実在するのに、Readが画像とPDFだけ「File does not exist」を返す。原因はPID namespaceと/procの食い違いで、テキストは読めるのが目印です。確かめ方と直し方を整理しました。
Readツールが File does not exist を返すのに、同じディレクトリのテキストファイルは普通に読めて、ls でも画像が見える。この食い違いが出たら、ファイルのパスではなく、Claude Codeが動いているPID namespaceと /proc の組み合わせを疑う場面です。
該当するのは、rootlessのrunc、bubblewrapで --unshare-pid を使いつつ親の /proc を共有する構成、Kubernetes上の入れ子のサンドボックスなどです。普通のローカル端末や、素直なDockerコンテナでは起きません。
症状は「画像とPDFだけ」が読めない
Claude CodeのIssue #102816に報告された症状は、次のとおりです。
- 画像とPDFをReadすると、
File does not exist. Note: your current working directory is /tmp.のように返る - 同じディレクトリのテキストファイルは読める
- Node.jsの
fs.open、readFile、realpathは画像に対して成功し、Bashのstatやlsもファイルを見つける - 2.1.181では同じサンドボックスで動いていた
つまりファイルは本当に存在します。エラー文が指すのは「ファイルがない」ですが、実際に失敗しているのは、Readが画像やPDFを開き直す途中の1手です。
報告者の環境は、Linux x86_64、ネイティブインストール、ユーザーとPIDの両方のnamespaceを持つrootlessのruncでした。親の /proc をそのままバインドマウントしており、親側の /proc がKubernetesにマスクされているため、入れ子のユーザー namespace内で mount -t proc を実行するとEPERMになる、という事情が添えられています。非対話モード(-p)で、First-party APIとBedrockの両方で再現しています。
原因は /proc/selfではなく /proc/PID/fdを使っていたこと
報告者がバンドルされたJavaScriptを読んで特定した原因は、強化されたファイル読み取りの経路にあります。この経路は、すでに開いたファイル記述子を /proc/<pid>/fd/<fd> という形で開き直します。この <pid> に process.pid、つまりそのnamespaceの中でのPIDを使っていました。
PID namespaceを分けても /proc が親のprocfsのままだと、/proc/<namespace内のPID> は存在しないか、別のプロセスを指します。開き直しはENOENTで失敗し、ツールは汎用の「見つからない」文言を返します。
同じ場所にあるシンボリックリンク確認の補助関数は、/proc/self/fd/<fd> を使っていて、どのnamespaceでも動きます。報告者は、Node.jsから見た差をこう示しています。
readFileSync('/proc/self/fd/'+fd) -> ok
readFileSync('/proc/'+process.pid+'/fd/'+fd) -> ENOENT提案された修正は、開き直しを /proc/self/fd/<fd> にするか、開いている記述子に対して fs.readFile(fd) を使うことでした。
テキストファイルが読める理由は、Issueからは断定できません。報告者は「テキストは同じディレクトリで読める」と書いているだけで、経路の違いまでは説明していません。画像とPDFが通る経路と、テキストが通る経路は別だと読むのが、現象に合う解釈です。
自分の環境が該当するか確かめる
Issueの再現手順は、Linuxなら専用のコンテナなしで試せます。まず小さなPNGを作り、普通に読ませます。
P='Use the Read tool on /tmp/img.png and reply with the word IMAGE if it returned an image, otherwise the first 120 characters of what it returned.'
claude -p "$P" --permission-mode default \
--allowedTools Read --add-dir /tmp --output-format json | jq -r .result画像として返れば IMAGE と出ます。次に、新しいPID namespaceで包んで同じことをします。
unshare -U -r -p -f claude -p "$P" --permission-mode default \
--allowedTools Read --add-dir /tmp --output-format json | jq -r .resultunshare -U -r -p -f は、ユーザー namespaceとPID namespaceを作りますが、/proc は親のものを引き継ぎます。影響を受けるバージョンでは、ここで File does not exist. Note: your current working directory is /tmp. が返ります。
--debug を付けても手掛かりは少なく、Issueに載っているのは outcome=error durationMs=3 と、Read tool error (3ms) という行だけです。3ミリ秒で失敗する点が、画像の中身を読んでから失敗したのではない、という傍証にはなります。
手元の /procが自分のnamespaceのものか調べる
Issueにある手順ではありませんが、同じ条件かどうかは次の短い確認でも当たりがつきます。自分のPIDと、/proc/self が指すPIDを並べます。
python3 -c 'import os; print(os.getpid(), os.readlink("/proc/self"))'2つの数字が一致すれば、/proc は自分のnamespaceのものです。食い違うなら、親のprocfsを共有しています。この確認で食い違っても、それだけでReadが失敗するとは限りません。影響を受けるバージョンかどうかと、組み合わせて判断してください。
bubblewrapで包んでいる場合は、起動オプションを見る方法もあります。--unshare-pid があるのに --proc /proc が無く、親の /proc を --bind や --ro-bind で渡していれば、Issueと同じ条件です。--proc はbubblewrapの一般的なオプションで、Issueが示した手順ではありません。Kubernetes上で入れ子にしている場合は、外側のコンテナが /proc をマスクしているかどうかが分かれ目になります。
直し方: 修正版へ上げる
修正の時期は、情報源ごとにずれています。次の表で並べます。
| 情報源 | 書かれていること |
|---|---|
| Issueの本文 | 書かれていること2.1.284で確認、2.1.299と2.1.301のバイナリで同じ構造を確認 |
| Issueの担当者コメント(2026-10-21) | 書かれていること2.1.302で修正済み。アップグレードして再度報告してほしい |
| 更新履歴の2.1.309(2026-10-22) | 書かれていること「親の /proc を共有するLinuxサンドボックスで、PDFのページ範囲指定のReadがFile does not existを返す問題を修正」 |
影響を受ける範囲は、Issueの題名が2.1.284〜2.1.301と示しています。報告者は2.1.181では同じサンドボックスで動いていたと書いており、2.1.284、2.1.299、2.1.301で同じ /proc/<pid>/fd/ の構造を確認しています。2.1.182〜2.1.283の間にどこで混入したかは、Issueに書かれていません。
担当者は画像とPDFをまとめて2.1.302で直ったと書いています。一方、更新履歴の2.1.302の節には、この問題に当たる項目が見当たりません。PDFのページ範囲に限った項目が2.1.309にあります。
画像とPDFの全ケースで直っているかは、この食い違いからは言い切れません。確実に避けたいなら、2.1.309以降へ上げて、上の再現手順を直し終わりの確認に使う、のが確実です。
claude --version
claude updateバージョンを上げて直らなければ、環境の情報を揃えたうえで、Issue #102816の担当者の呼びかけどおり報告するのが近道です。
上げられないときの回避策
サンドボックスの構成を触れる場合は、「/proc が自分のnamespaceのものではない」という条件そのものを外す方向が考えられます。次の2つは、Issueが示した条件から導いた方向で、動作確認済みの手順ではありません。
- 新しいprocfsをマウントする: bubblewrapなら、親の
/procをバインドする代わりに--proc /procで新しいprocfsを張る構成が、条件から外れます - PID namespaceを分けない: 外側のサンドボックスがすでに隔離しているなら、内側で
--unshare-pidやunshare -pを重ねない構成にします
Issueの環境は、親の /proc がKubernetesにマスクされていて、入れ子の中でprocfsを新しくマウントできませんでした。この制約下では、1つ目の方向は使えません。
どちらも取れないなら、画像やPDFを読ませる前に、テキストへ変換しておくのが現実的です。PDFなら pdftotext input.pdf output.txt のようにテキストを抜き出し、画像ならOCRの結果を読ませる形です。Claudeが画像を直接見る利点は失われます。
サンドボックスの種類ごとの違いはClaude Codeのサンドボックス環境の比較に、Docker内での入れ子の設定はenableWeakerNestedSandboxとbwrapPathの解説にあります。
似たエラーとの切り分け
画像とPDFのReadで出るエラーは、文言が似ていても原因が違います。
| 画面に出る文言 | 起きていること | 見る場所 |
|---|---|---|
File does not exist(画像・PDFだけ) | 起きていることPID namespaceと /proc の食い違い | 見る場所この記事 |
File is too large to read as an image | 起きていること画像ファイルが128MB超 | 見る場所ツールのリファレンス |
Cannot read this as an image | 起きていることパイプやデバイスが128MB超を送り続けた | 見る場所パイプ・デバイスの切り分け |
pdftoppm is not installed | 起きていることPDFのページ範囲指定にpoppler-utilsが必要 | 見る場所ツールのリファレンス |
最後の行は、見落とされやすい点です。10ページを超えるPDFをページ範囲で読む場合、Readは pdftoppm でページを描画します。入っていなければ、apt-get install poppler-utils などで入れます。この失敗は File does not exist とは文言が違うので、混同しないでください。
切り分けの順番は、短くまとめられます。
Readの失敗の切り分け
- Qテキストファイルは同じ環境で読めるか?答えがパス、権限、denyルールを先に見直す答えがはい
読めるなら次へ進みます。
- Q画像やPDFだけが File does not exist になるか?答えが上の表で別の文言を探す答えがはい
該当するなら次へ進みます。
- QPID namespaceを分け、親の /proc を共有しているか?答えが別の原因を疑う答えがはい
共有しているなら、この記事の条件に合います。
2.1.309以降へ上げる
直らなければ、上の再現手順の結果を添えてIssueに報告します。
エラー文が原因を指していない点が、いちばん厄介
この不具合は、実装の1行の取り違えですが、運用では長く効きます。エラー文が「ファイルがない」と言うので、人間もClaudeも、パスの打ち間違いや、作業ディレクトリのずれを疑って時間を使います。
Issueの「影響」の節も、同じ点を挙げています。セッションごとにPID namespaceを割り当てるオーケストレーターでは、画像とPDFの読み取りが丸ごと失われ、しかもエラー文は実在しないファイルのせいにする、という指摘です。
AIエージェントを隔離環境で動かす構成が増えるほど、こうした「隔離の作り方」と「ツール側の前提」のずれは出やすくなります。サンドボックス内で画像だけが読めないときは、パスより先に、隔離の境界を疑う順番に切り替えると、原因に早く行き着けます。
次に確かめること
- 影響を受けた構成が、親の
/procを共有していたか(上の確認コマンドで当たりをつける) - 使っているバージョンが2.1.309以降か
- 上げたあと、再現手順の
unshare版がIMAGEを返すか
関連する記事
Claude Code をもっと見る →Claude Codeとは — できること・料金・始め方と使い方の全体像
「Cannot read this as an image」の原因と128MB上限 — Claude Code
Plugin not loadedの原因と直し方 — Claude Codeのmanifest・ELOOP・EACCES
Timed out after 60s waiting for AWSが出る原因 — Bedrockウィザードの切り分け
Claude CodeがVirtualBoxでGeneral Protection Faultになる原因
Claude CodeがARM64環境で固まる・SIGILLになるときの切り分け