こんな症状、見たことありませんか
macOS + MAMP + Laravel(Composer)のローカル開発で、ある日突然ブラウザが真っ白な 500 を返すようになる。
Notice: require(): Read of N bytes failed with errno=11 Resource deadlock avoided
in .../vendor/composer/autoload_real.php on line 12
Fatal error: Uncaught Error: Failed opening required '.../vendor/composer/ClassLoader.php'
厄介なのはここからです。
- リロードしても直らない(エラーが起きるファイルが毎回微妙に変わることすらある)
- ターミナルで
composer dump-autoloadを叩くと ハングする -
cat vendor/composer/ClassLoader.phpは普通に読める(=「CLIでは開けるから大丈夫」ではない) - 前日まで何の問題もなく動いていた
コードは一切変えていないのに起きる系のバグで、最初は「Composer が壊れた?PHPのバージョン問題?」と当たりを付けて時間を溶かしました。結論から言うと 原因はコードではなく、プロジェクトの置き場所 でした。
原因:~/Documents 配下 + ファイル同期系サービスの組み合わせ
errno=11 は Linux/macOS カーネルが返す EDEADLK(デッドロック回避)です。今回のケースでは、以下のような「ファイルを inode レベルで掴み続ける」サービスが ~/Documents を監視下に置いていることが根本原因でした。
| 容疑者 | 何をしているか |
|---|---|
| iCloud Drive の「デスクトップと書類フォルダ」同期 |
~/Documents 全体を File Provider 管理下に置く |
Google Drive の File Stream(DFSFileProviderExtension) |
同様に Documents を監視・同期 |
| Time Machine のローカルスナップショット | APFS の COW(copy-on-write)参照を保持 |
| Spotlight のインデクサ | 大量ファイル変更時に再インデックスで I/O を握る |
これらが APFS のコピーオンライト参照やファイルロックを保持していると、Apache の並列 worker が mmap() で同じファイルを読もうとした瞬間にカーネルがロック取得を諦めて EDEADLK を返します。
ポイントは、CLI の単発 read()(cat や PHP の file_get_contents)は通ってしまうこと。Apache のマルチプロセス + mmap の組み合わせのときだけ再現するので、「ターミナルでは読めるし壊れてないはず」という調査の勘が裏切られます。vendor/ は composer install の度に大量のファイルが新規 inode で作られるため、標的にされやすいディレクトリでもあります。
応急処置(その場をしのぎたいとき)
引っ越しがすぐできない場合は、上から順に試すと大体直ります。
# ① Time Machine の進行中バックアップを止める
tmutil stopbackup
# ② 残っているローカルスナップショットを削除(root 不要)
tmutil listlocalsnapshots /
tmutil deletelocalsnapshots <YYYY-MM-DD-HHMMSS> # 出てきた ID を指定
# ③ 今後のバックアップ対象から除外
tmutil addexclusion /path/to/project
tmutil isexcluded /path/to/project # [Excluded] と出れば OK
# ④ autoload ファイルだけ新しい inode に置き換える(最後の手段)
cd <project>/vendor/composer
for f in *.php; do cp -p "$f" "$f.tmp" && mv "$f.tmp" "$f"; done
# ⑤ Apache を Stop → Start(OPcache をクリア)
ただし、これは絆創膏です。composer install / npm install / ファイル編集のたびに新しい inode が File Provider に再補足され、また再発します。
恒久対処:プロジェクトを ~/Documents の外に出す
一番効いたのは単純で、プロジェクトを File Provider の監視が及ばない場所(今回は MAMP の htdocs 配下)に移設することでした。
# 1. MAMP の Apache を Stop(GUI から。CLI の apachectl restart はポート80衝突するので避ける)
# 2. 新ロケーションに clone(mv は File Provider に阻まれてハングすることがあるので使わない)
cd /Applications/MAMP/htdocs
git clone <リモートURL> myapp
cd myapp
# 3. 依存を新ロケーションで再生成(File Provider 圏外なのでクリーンに通る)
composer install
nvm use 22 # node は 20+ 推奨。古い v10 だと vite build が壊れる
npm ci
npm run build
# 4. Laravel のコンパイル済みキャッシュをクリア(旧パスの焼き込み除去)
php artisan optimize:clear
# 5. vhost の DocumentRoot を新パスへ書き換える
# /Applications/MAMP/conf/apache/extra/httpd-vhosts.conf
# 6. Apache を Start → 動作確認
curl -sL -o /dev/null -w "%{http_code}\n" http://your-local-domain # 200
.env は git 管理外なので、移設前に控えておくのを忘れずに。DB 接続はパスに依存しないので、移設後もそのまま使えます。
git worktree で配信している場合の注意
Claude Code などで git worktree から配信していると、worktree は .git に 絶対パス で参照を持っているため、単純な mv で壊れます。対処は2択:
- 通常チェックアウト1本で配信するようにする(worktree 配信をやめる。一番シンプルで事故りにくい)
-
git worktree moveで worktree ごと正しく移設する(.gitの参照も書き換わる。ズレたらgit worktree repair)
複数プロジェクトを並行して worktree 配信していると管理が煩雑になりがちなので、通常チェックアウトでの配信を基本にする方をおすすめします。
再発防止チェックリスト
-
プロジェクトは
~/Documentsの外にある -
~/Documents配下に何か残す場合は Time Machine 除外済み(tmutil isexcludedで確認) -
node は毎回
nvm useでバージョンを固定してから build - iCloud「デスクトップと書類」同期・Google Drive File Stream の対象範囲を見直す(開発中の重いリポジトリは外に出す)
まとめ
errno=11 Resource deadlock avoided は、一見 Composer や PHP のバグに見えて、実態は macOS のファイル同期系サービスと Apache の mmap の相性問題 でした。「CLI では読めるのに Apache だけ死ぬ」「昨日まで動いてた」という組み合わせを見たら、まずプロジェクトの置き場所を疑ってみてください。