Claude Media
Claude Code File does not existが画像とPDFで出る原因と対処

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 .result

unshare -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の失敗の切り分け

  1. Qテキストファイルは同じ環境で読めるか?
    答えがパス、権限、denyルールを先に見直す

    読めるなら次へ進みます。

    答えがはい
  2. Q画像やPDFだけが File does not exist になるか?
    答えが上の表で別の文言を探す

    該当するなら次へ進みます。

    答えがはい
  3. QPID namespaceを分け、親の /proc を共有しているか?
    答えが別の原因を疑う

    共有しているなら、この記事の条件に合います。

    答えがはい

2.1.309以降へ上げる

直らなければ、上の再現手順の結果を添えてIssueに報告します。

エラー文が原因を指していない点が、いちばん厄介

この不具合は、実装の1行の取り違えですが、運用では長く効きます。エラー文が「ファイルがない」と言うので、人間もClaudeも、パスの打ち間違いや、作業ディレクトリのずれを疑って時間を使います。

Issueの「影響」の節も、同じ点を挙げています。セッションごとにPID namespaceを割り当てるオーケストレーターでは、画像とPDFの読み取りが丸ごと失われ、しかもエラー文は実在しないファイルのせいにする、という指摘です。

AIエージェントを隔離環境で動かす構成が増えるほど、こうした「隔離の作り方」と「ツール側の前提」のずれは出やすくなります。サンドボックス内で画像だけが読めないときは、パスより先に、隔離の境界を疑う順番に切り替えると、原因に早く行き着けます。

次に確かめること

  • 影響を受けた構成が、親の /proc を共有していたか(上の確認コマンドで当たりをつける)
  • 使っているバージョンが2.1.309以降か
  • 上げたあと、再現手順の unshare 版が IMAGE を返すか
この記事を共有:XはてブLinkedIn