不具合の内容
Visual Studio Code v1.82 から Visual Studio Code を動作させている
Node.js のバージョンが 18 系にアップグレードされた。
これにより、拡張機能から TypeError: Failed to parse URL from ...
という
エラーが発生する場合がある。
ほとんどの場合に、このエラーが発生した拡張機能は正常に動作しなくなる。
実際に遭遇したエラー
Ruby 拡張機能 (https://marketplace.visualstudio.com/items?itemName=rebornix.Ruby)
を使っているプロジェクトで次のエラーが表示されていた。
TypeError: Failed to parse URL from <VSCODE_DIR>/extensions/rebornix.ruby-0.28.1/dist/server/tree-sitter.wasm
TypeError: Failed to parse URL from <VSCODE_DIR>/extensions/rebornix.ruby-0.28.1/dist/server/tree-sitter.wasm
stacktrace ...
abort(TypeError: Failed to parse URL from <VSCODE_DIR>/extensions/rebornix.ruby-0.28.1/dist/server/tree-sitter.wasm). Build with -s ASSERTIONS=1 for more info.
(Use `node --trace-uncaught ...` to show where the exception was thrown)
Node.js v18.15.0
Tip
Ruby 拡張機能rebornix.Ruby
は 2022年の4月で開発が止まっている。
Ruby LSP 拡張機能 (https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
への移行が推奨されているが、この拡張機能に必要なruby-lsp
gem は Ruby 3.0 を要求している。
残念ながらメンテナンスしていたプロジェクトは Ruby 2 系で動いている上、
Ruby のアップグレードも難しかった。
原因の調査
エラーメッセージから tree-sitter.wasm
という WASM ファイルがエラー原因に関わっている。
tree-sitter (https://tree-sitter.github.io/tree-sitter/) は構文解析ツールで、
複数の実行環境をサポートしている。
サポートされる実行方法の1つに Javascript 向けの WASM 形式ファイルを配布しているので、
このファイルが原因のようだと目星をつけた。
tree-sitter は Rust で書かれていたが、調べてみると WASM は
Emscripten (https://emscripten.org/) というツールで生成されているようだった。
このリポジトリで Failed to parse URL from
で検索すると、それっぽい issue が見つかった。
Emscripten の詳細な実装は調べていないが、 Emscripten が生成した WASM はいくつかの実行環境を
サポートすることができる。
実行環境の判別は WASM の実行時に行われるようだが、
その判定で fetch
が定義されていなければ Node.js 環境というような分岐になっていた。
しかし、 Node.js v18 で fetch
API が追加されたことにより実行環境の判定が意図しない動作になり
Node.js が追加した fetch
が呼び出しされてエラーが発生するようになった様子。
この不具合は Emscripten v3.1.13 で修正されているので、それ以降のバージョンの
Emscripten で生成された WASM であれば、今回のエラーは発生しないと思われる。
Tip
ちなみに、 WEB ブラウザのfetch
であればfetch("foo.wasm")
と書くと
現在の URL からの相対パスにあるfoo.wasm
へのリクエストと解釈されるのでエラーにならない。
Emscripten はこの仕様を利用して、現在のサイトに存在する.wasm
ファイルをダウンロードする
目的でfetch
を呼び出ししていると思われる。しかし、 Node.js の
fetch
やnode-fetch
といったライブラリのfetch
は
foo("foo.wasm")
はエラーになる。
これはfetch
の仕様にfile://
スキームのサポートが明記されていないこと、
fetch
でローカルファイルへのアクセスを行うことはセキュリティホールになりかねないことから
意図的な実装の様子。
- file:// URLs · Issue #75 · node-fetch/node-fetch · GitHub
- ability to fetch `file://` URLs in node `fetch`. · Issue #48554 · nodejs/node · GitHub
よって、 Node.js の
fetch
はfetch("foo.wasm")
のようなスキーム指定が無い
URI は受け付けないのでFailed to parse URL from ...
というエラーを発生させるように
なっている。
さらに Node.js でfile://
スキームをつけてfetch("file://foo.wasm")
としても、
TypeError: fetch failed
,cause: Error: not implemented... yet...
が発生して動作しない。
よって Node.js のfetch
でローカルファイルを取得する事は出来ない。
問題の回避策
Node.js v18 で fetch
が追加されたことが主な原因なので、 Node.js が定義する fetch
が
なくなればいいということになる。
node
の実行時に --no-experimental-fetch
オプションを指定すると、
Node.js は fetch
API を定義しなくなるので、この方法で回避できる。
ただし、 Visual Studio Code の実行時に Node.js のオプションを指定する方法は
提供されていない様子。
Node.js 事態が提供している NODE_OPTIONS
環境変数をセットすることで
Visual Studio Code を実行する Node.js 環境で fetch
API の定義を回避できる。
ただし、 Visual Studio Code が Node.js v18 で実行されるようになったので、
拡張機能などでも fetch
API を呼び出しするようになる可能性がある。
fetch
API を使用する拡張機能が存在している場合に、
NODE_OPTIONS=--no-experimental-fetch
をセットしていると
他のバグを誘発する恐れがある点には注意が必要。
この回避方法を使うのであれば Visual Studio Code の Remote Development 機能を利用して
Docker コンテナといった限定的な環境で開発をするといった環境分離措置を取った方がいいと思われる。
.devcontainer/devcontainer.json
で Docker コンテナで NODE_OPTIONS
をセットする例:
"containerEnv": {
"NODE_OPTIONS": "${containerEnv:NODE_OPTIONS} --no-experimental-fetch"
}
docker-compose.yml
で Docker コンテナで NODE_OPTIONS
をセットする例:
services:
foo-service:
environment:
NODE_OPTIONS: "--no-experimental-fetch"
余談
Emscripten で不具合されたバージョン v3.1.13 がリリースされたのが2022年6月のようなので、
この不具合に遭遇するのは該当のバージョン以降で .wasm
を生成していない拡張機能に限定される。
Visual Studio Code v1.82 のリリースが 2023年8月なので、
このエラーが発生する拡張機能は1年以上は新しいリリースをしていないと考えられる。
拡張機能のメンテナンスが続いていればいいが、メンテナンスが止まっているようなら
代替の拡張機能に移行したほうがいいかもしれない。