GitHub Actionsでテスト用のジョブを動かした時に`Process completed with exit code 139.`エラーが発生した場合の対処法
GitHub Actionsでテスト用のジョブを動かしていた時に、全てのテストが成功しているにもかかわらず、次のようなエラーが発生してジョブが失敗したことになってしまった
Segmentation fault (core dumped) 97 Error: Process completed with exit code 139.
TL;DR
最終的な原因はRustで書かれた@node-rs/bcrypt
パッケージをインポートしているファイルに置いてある関数のスパイを作成したことが原因だった。
なので、原因のパッケージ全体をモックすることで解決する。
// test/setup-test-env.ts ... vi.mock("@node-rs/bcrypt", () => { return { hash: vi.fn().mockRejectedValue("password-hash"), verify: vi.fn().mockReturnValue(true), }; });
以下では、どういった流れで原因を特定できたかを忘れないための自戒としてネットの海に漂流させておくことにする。
Segmentation fault (core dumped)
とは一体なんぞや?
調べてみると、Segmentation fault 11 (core dumped)
とは、本来アクセスしちゃいけないメモリのアドレスにアクセスしようとした時に発生してしまうエラーの類らしい。
segmentation fault 11(core dumped)(以下、セグフォ)とは、本来はアクセスできないメモリのアドレスにアクセスする時に起こるエラーのことを言う。 www.toumasu-program.net
そして我らがStackoverflowにも次のようなコメントを発見。
Another possible cause (which I encountered today) is that you’re trying to read/write a file which is open. In this case, simply closing the file and rerunning the script solved the issue.
以上の内容を踏まえて、ファイルの生成や更新関連でエラーが発生しているのかと考えた。 そして、テスト時のファイルの生成に関連がありそうなコンポーネントのスナップショットテストをいったんスキップするように設定した。
しかし結果は変わらず。
別のエラーの発生
続いて、エラーの直前で終わっているテストに狙いを絞ることにした。 そのファイル内で書かれていた複数のテストをコメントアウトして、ひとつのテストのみを実行するように修正した。
実行しみると、今度はSESSION_SECRET
がセットされていないというエラーが発生していた。
これは内部でcreateCookieSessionStorage
にsecrets
を指定する際、環境変数のSESION_SECRET
を参照しているにも関わらず環境変数が読み込めない時に発生するエラーだった。
if (!sessionSecret) { throw new Error("SESSION_SECRET must be set"); ^ }
もしかしたら、設定されていない環境変数にアクセスしようとしたからSementation fault
エラーが出たのかもしれないと思ったが、もちろん違った。
つまり上記のエラーに関しては、通常通りのテスト失敗としてカウントされた。今回のテーマとは何の関係もないエラーだった。
メモリ不足の可能性
GitHub ActionsでJestを使用した際に頻繁にSementation fault (core dumped)
が発生していると書かれたIssueを見つけた。
もしかしたら、Node.js実行時のヒープ領域のメモリ容量が不足している可能性があるので、テストの実行時にNODE_OPTIONS
環境変数を設定し、—-max-old-space-size
を通してメモリ容量の確保を試みた。
# .github/workflows/deploy.yml ... - name: Run vitest run: npm run test -- --coverage env: NODE_OPTIONS: --max-old-space-size=4096 ...
もちろん解決しなかった。
@remix-run/server-runtime
からjson
をインポートしているのが原因なのか、vi.setSystemTime
を利用しているからなのか色々試してみたが、どれも検討外れだった。
問題を特定
最終的に、単体でGitHub Actionsで実行したときにエラーが発生するテストとエラーが発生しなかったテストの差分を調べた。
するとエラーが発生するテストにおける、テスト対象の関数のファイル内でのみ@node-rs/bcrypt
パッケージをインポートしていることがわかった。
そしてそのエラーは、、そのテスト対象の関数のスパイを作成した時にだけ発生するエラーであることがわかった。
@node-rs/bcrypt
とは
Rustで実装されたbcryptの実装パッケージである。
Node.jsにおける最も高速なbcrpt
であると謳っている。
napi-rs
を使用してRustで作成されているNode.jsアドオンであり、他にもdeno-lint2
などのパッケージが@node-rs
配下で公開されている。
解決方法
なぜ、このようなエラーが発生するかはわからないが、vitestに設定したセットアップファイルであるsetup-test-env.ts
内で次のようにモックをすることにした。
// test/setup-test-env.ts ... vi.mock("@node-rs/bcrypt", () => { return { hash: vi.fn().mockRejectedValue("password-hash"), verify: vi.fn().mockReturnValue(true), }; });
これによりSegmentation Fault
エラーが発生せずにGitHub Actions上でジョブが正常に終了することが確認することができた。
まとめ
この問題を特定するために数時間も費やしてしまったことが一番の後悔であった。
もし同じような問題に直面してしまった誰かにとってこの記事が一助にでもなるのなら、せめてもの救いである。