不具合の内容
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-lspgem は 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年以上は新しいリリースをしていないと考えられる。
拡張機能のメンテナンスが続いていればいいが、メンテナンスが止まっているようなら
代替の拡張機能に移行したほうがいいかもしれない。