【メモ】WSL環境での実行時にWindows側のプログラムを参照してしまう謎現象

割とハマったのでメモ

Windows11上のWSLのUbuntu24.04でshファイル内でnpm run startを実行していて以下のようなエラーに遭遇。

git cloneコマンドで持ってきたプログラムだったのですが、他の環境ではこのようなエラーは発生していませんでした。

エラーの内容としてWindows側のプログラムを起動しようとしているみたいです。ファイルがなかったので何も発生しませんでしたが。

エラー表示

$ ./start_service.sh

> y-websocket@2.0.4 start
> node ./bin/server.cjs

'\\wsl.localhost\Ubuntu24.04-TEST\home\user2404\y-websocket'
上記の現在のディレクトリで CMD.EXE を開始しました。
UNC パスはサポートされません。Windows ディレクトリを既定で使用 します。
node:internal/modules/cjs/loader:1147
  throw err;
  ^

Error: Cannot find module 'C:\Windows\bin\server.cjs'
    at Module._resolveFilename (node:internal/modules/cjs/loader:1144:15)
    at Module._load (node:internal/modules/cjs/loader:985:27)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
    at node:internal/main/run_main_module:28:49 {
  code: 'MODULE_NOT_FOUND',
  requireStack: []
}

Node.js v20.10.0

想定していなかった動作でしたが、実際に発生することが確認できました。確かに、Windows側からWSLにあるプログラムを実行するのは可能です。

【参考】 このときはWSL上のsha256sumを実行していました。 uepon.hatenadiary.com

今度は逆の現象でWSL側からWindowsのバイナリを実行しようとしていた。(でも、存在していなかったので失敗している)

わかったこと

WSLディストリビューション上にないバイナリで、Windowsにあるバイナリがある場合にはそちらを動作させる可能性があるということです。

今回は、npmがその原因になっていました。whichコマンド(ファイルのパス確認)で見たところWSL上のパスではなく、Windows上にあったことで明確になりました。

いつもは必要なコマンドを実行して(バージョン表示など)、アプリの有り無しを判定していたのですが、このような現象が起き得るとなるとかなり厳しいですね。whichコマンドを使用したほうがいいような気がしてきました。

特に怖いのはnpmシェルスクリプト(*.sh)などで実行プログラムがいくつもあるときかもしれません。

どうすれば回避できるか?

以下のようにするのが対処療法としてはいいかなと思います。

# 現在の環境変数PATHを確認する方法
echo $PATH

# Windowsのパスが含まれているか調べる方法
which node
which npm

# シェルスクリプト内でパスを明示的に指定する方法
#!/usr/bin/env bash
export SHELL=/bin/bash
export PATH=/usr/local/bin:/usr/bin:$PATH

こんな感じでしょうか。ちょっと面倒ですね😥。

恒久的な対策

これなら、Windows側のプログラムが動か異様にしてしまえばというのが次の手です。

以下の設定では、以下のようなことを行います。

  • WSLWindowsの相互運用性は維持(enabled = true
  • WindowsのPATHは環境変数に追加されない(appendWindowsPath = false

1. /etc/wsl.confの修正・追記

# rootで/etc/wsl.confを編集
$ sudo nano /etc/wsl.conf

/etc/wsl.conf

# 以下の内容を設定(既存の内容があれば上書き)
[interop]
enabled = true
appendWindowsPath = false

2. WSLのシャットダウンと再起動:

# PowerShellまたはコマンドプロンプトで実行
PS> wsl --shutdown

3. WSLを再起動:

4. 設定の確認:

# 設定が反映されているか確認
$ cat /etc/wsl.conf

# PATHにWindowsのパスが含まれていないことを確認(```mnt```が入っているとWindowsのバイナリが動く設定です)
$ echo $PATH | tr ':' '\n' | grep '^/mnt'

この設定で、WSL環境内でのコマンド実行時にWindows側のプログラムが優先されることを防ぐことができます。

おまけ

Node.jsnpmのチェック用のスクリプトになります。 確認するプログラムを変更すれば、他でも使えるかもしれません。

check_environment.sh

#!/bin/bash

# check_environment.sh
echo "開発環境チェックを開始します..."

# 1. Node.jsとnpmの場所チェック
echo "=== Node.js/npm パスチェック ==="
NODE_PATH=$(which node)
NPM_PATH=$(which npm)

echo "Node.js: $NODE_PATH"
echo "npm: $NPM_PATH"

# Windows パスチェック
if echo "$NODE_PATH" | grep -q "/mnt/c/" || echo "$NPM_PATH" | grep -q "/mnt/c/"; then
    echo "警告: Windows側のNode.js/npmが使用されています"
    echo "  × npm path: $NPM_PATH"
    echo "対策: 'sudo apt install nodejs npm' を実行してWSL側にインストールしてください"
    exit 1
fi

# 2. バージョン情報の表示
echo -e "\n=== バージョン情報 ==="
echo "Node.js: $(node -v)"
echo "npm: $(npm -v)"

echo -e "\n✅ すべてのチェックが完了しました"

おわりに

WSL環境でnpmを実行した際に予期せずWindows側のパスを参照するという問題に出会いました。WSLWindowsの相互運用性には便利な面がある一方で、予期せぬ動作を引き起こす可能性もありそうです。実行されるバイナリのPATHを明示的にしないとやばい自体になるかもしれません。whichコマンドによるPATHの確認や、場合によっては/etc/wsl.confを編集することも検討したほうがいいのかもしれません。

一番簡単な方法は、WindowsネイティブにWSLで使用するようなバイナリをインストールせず、WSLDockerなどを使用するということかもしれません🤔