わすれっぽいきみえ

みらいのじぶんにやさしくしてやる

WSLでnpm startしたらELIFECYCLE errno 1のエラーが出たときの対処法

急に思い立ってNext.jsを触ってみようと思ったが、React.jsも触ったことがなかったのでReact.jsのチュートリアルから始めることにした。そこでチュートリアル通りにnpm startしたところで以下のようなエラーが出た。

Starting the development server...

events.js:292
      throw er; // Unhandled 'error' event
      ^

Error: spawn cmd.exe EACCES
    at Process.ChildProcess._handle.onexit (internal/child_process.js:268:19)
    at onErrorNT (internal/child_process.js:468:16)
    at processTicksAndRejections (internal/process/task_queues.js:84:21)
Emitted 'error' event on ChildProcess instance at:
    at Process.ChildProcess._handle.onexit (internal/child_process.js:274:12)
    at onErrorNT (internal/child_process.js:468:16)
    at processTicksAndRejections (internal/process/task_queues.js:84:21) {
  errno: -13,
  code: 'EACCES',
  syscall: 'spawn cmd.exe',
  path: 'cmd.exe',
  spawnargs: [ '/s', '/c', 'start', '""', '/b', 'http://localhost:3000/' ]
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! my-app@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the my-app@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/kimikimi714/.npm/_logs/2020-05-30T*-debug.log

このようなエラーが出たときの対処方法について書く。

単にnode_modulesの中身が壊れてるとき

これは適当な単語でググると方法は簡単に出てくる。ほとんどの人はこれで解決しそう。

$ rm -rf node_modules
$ rm package-lock.json
$ npm cache clear --force
$ npm install

要するにjsライブラリを入れ直すだけ。これで済むなら話は早いんだが、私の場合はこれでは治らなかった。というかチュートリアル通りにしかやってないのに、いきなりjsライブラリの依存関係がおかしいとは考えにくくて、一応やってはみたけどはじめからこれは解決策にならないだろとは思っていた。

cmd.exeのパーミッションがないとき

最初に書いたエラーにあるspawn cmd.exe EACCESがポイントだった。

私はWSLの仕組みを完全には理解してなかったのでnpm startを実行したときに呼び出されるコマンド群はWSL上にあるUbuntuに閉じているんだろうと思い込んでいた。だがcmd.exeは拡張子からしてもWindowsで、ここに気づいてからエラーについて調べ方を変えた。

www.atmarkit.co.jp

ここを読むとわかるように、とくに何もしなくてもWSLからフルパスでcmd.exeを呼び出せる。しかしエラーが出ているわけなので、私の環境のcmd.exeの状態がおかしいのではないかと疑い調べてみた。すると以下のようになった。

$ which cmd.exe
(何も表示されない)ls -la /mnt/c/WINDOWS/system32/cmd.exe
-r--r--r-- 2 kimikimi714 kimikimi714 289792 Dec 7 18:08 /mnt/c/WINDOWS/system32/cmd.exe

cmd.exeにread権限しかない…。この時点でもしかすると過去にやったwsl.confによるファイルパーミッションのマスクが関係しているかもしれないと思った。

現時点でのwsl.confの中身は以下のようになっている。

$ cat /etc/wsl.conf
[automount]
options = "metadata,umask=22,fmask=111"

Ubuntu側から見たときに作ったファイルのパーミッションがなんでも755になってしまうのを避けたかったからこの設定を入れた。だが、この設定がなければcmd.exeの実行権限がおそらく正常に付与されるんじゃないか?と思って、wsl.confの設定を無効化してみることにした。無効化するには/etc/wsl.confを適当なファイル名に変更し、Ubuntuを終了してLxssManagerを再起動し、Ubuntuを起動しなおすとよい。そのうえでcmd.exeの状態を確認すると以下のようになった。

$ which cmd.exe
/mnt/c/WINDOWS/system32/cmd.exe

$ ls -la /mnt/c/WINDOWS/system32/cmd.exe
-r-xr-xr-x 2 kimikimi714 kimikimi714 289792 Dec 7 18:08 /mnt/c/WINDOWS/system32/cmd.exe

今度はcmd.exeに実行権限が付与された。fmaskの値が利用されないんだから当たり前か。この状態でReact.jsのチュートリアルを立ち上げるためnpm startを実行すると以下のようになる。

Compiled successfully!

You can now view my-app in the browser.

Local:            http://localhost:3000
On Your Network:  http://XXX.XXX.XXX.XXX:3000

Note that the development build is not optimized.
To create a production build, use npm run build.

f:id:kimikimi714:20200531142109p:plain:w200
チュートリアルが正しく起動したら出てくる画面

http://localhost:3000 でReact.jsチュートリアルの最初の画面が出るようになる。やはりnode_modulesは関係がなかった*1

cmd.exeに実行権限を付与しつつ、ほかのjsファイルなどに勝手に実行権限が付与されないようにしたい場合

wsl.confを無効化したままにしておくことは個人的にやりたくないので、cmd.exeはrootでなら実行権限を付与しつつ、ほかのjsファイルなどに実行権限が付与されないようなfmaskの値にwsl.confを書き変えればよい。

www.atmarkit.co.jp

にumaskの値をどう使えばいいのかが記載されてるので、これに従ってwsl.conを以下のように書き変える。

$ cat /etc/wsl.conf
[automount]
options = "metadata,umask=22,fmask=11"

これでcmd.exeの権限を確認すると

$ ls -la /mnt/c/WINDOWS/system32/cmd.exe
-r-xr--r-- 2 kimikimi714 kimikimi714 289792 Dec  7 18:08 /mnt/c/WINDOWS/system32/cmd.exe

期待値通りのマスク結果になった。この状態でnpm startを実行するとちゃんと実行される。

個人的にはrootの実行権限だけ残せばnpm startが動く理由についていまいち腹落ちしてない。とくにsudo npm startのようにLinux側の管理者権限で実行したわけじゃないのにcmd.exeが呼び出せるというのが直感に反していて気持ち悪さがある。しかしそもそもWindowsLinuxファイルシステムは異なるものなので、実行権限が少しでもついてるのがWindows側から見ると重要なんだと思う。ユーザーの考え方も違うのを、やんごとなきやり方でバインドしてるんだろう。もっとちゃんと仕組みを勉強したらいいと思うけど、やりたいのはNext.jsを使うことなので、WSL上のLinuxWindows上のコマンドがどういう仕組みで連動してるのかについては誰か詳しい人がいたら教えてほしい。ユーザー周りとかものすごいやり方を取ってそう。

本当は/mnt/c全体に適応されるマスクではなくて、特定ディレクトリにだけ適応できる(今回ならReact.jsのチュートリアルが動いてるディレクトリかcmd.exeが動くようなWindowsのPATH)だけパーミッションマスクできればいいんだけど、調べてもあんまり有益そうなのは出てこなかった。/etc/fstabでマウント設定をいろいろいじれるのでCドライブの特定ディレクトリとCのパスを分けるとかできればいいかもしれないが、それはそれで設定が爆発しそうなので、ここいらが引き際だなとも思ってやめた。

*1:ちなみにNode.jsのバージョンがまずいのか?とも思ってv12.17.0からv14.3.0に変えたりもした。でもまったく関係なかった。