「astro buildに失敗」という通知が嘘をついていた話 — 真因はnpmが見えていないことだった

  • #launchd
  • #Node.js
  • #自動化
  • #常駐サーバ
  • #デバッグ
「astro buildに失敗」という通知が嘘をついていた話 — 真因はnpmが見えていないことだった

この記事について

常駐サーバのlaunchdジョブとして、内部リンク補強スクリプトを毎日回している。ある日から、正当なリンク修正の自動マージが毎回見送られ、Discordには「astro buildに失敗」とだけ通知が届くようになった。ビルド設定を疑って調べたが、原因はビルドエラーではなく、そもそもビルドコマンドを実行するnpmという実行系そのものが見えていないことだった。

何が起きていたか

常駐サーバのnode/npmはnvmで管理している。nvmはシェルの起動時にPATHへnodeのバージョンディレクトリを差し込む仕組みだが、launchdのジョブは通常のログインシェルを経由しない。launchdが渡す環境変数は最小構成のPATHだけで、nvmのシェル初期化スクリプトは一度も実行されない。結果として、ジョブの中でnpm run buildを叩いた瞬間に

npm: command not found

で落ちていた。ところがジョブ側のエラーハンドリングは、このコマンド失敗を「astro buildに失敗した」という一般的な文言でDiscordに流していたため、実行系が無いという環境問題が、ビルド設定の不具合であるかのように見えていた。

症状と真因のズレ

これは「エラーメッセージが指す場所」と「真因の場所」がずれる典型的な形だった。

見えていたもの実際の場所
「astro buildに失敗」という通知文言ビルドコマンド(npm run build)そのものが起動できていない
ビルド設定・依存関係を疑う調査の動線PATH解決の問題であり、Astro側の設定には一切問題がなかった

この間、正当な内部リンク修正PRがビルド失敗を理由に自動マージされず反映されないままになっていた。通知文言が具体的すぎたことで、逆に本当の原因(実行系の不在)への意識が向きにくくなっていた。

対策:自己解決 + フェイルセーフ

対処は2段構えにした。まず、npmがPATHに見つからない場合に、nvmが管理するnodeのバイナリを自己解決してPATHへ足す。

# node/npm を頑健に解決して PATH に足す
if ! command -v npm >/dev/null 2>&1; then
  NODE_BIN="${NT_MEDIA_NODE_BIN:-}"
  [ -z "$NODE_BIN" ] && NODE_BIN="$(dirname "$(ls -t "$HOME"/.nvm/versions/node/*/bin/node 2>/dev/null | head -1)" 2>/dev/null || true)"
  [ -n "$NODE_BIN" ] && [ -x "$NODE_BIN/npm" ] && PATH="$NODE_BIN:$PATH"
fi

ls -t でnvmのバージョンディレクトリのうち最新のnodeバイナリを取り、そのbinディレクトリをPATHの先頭に追加する。環境変数で明示的にNode本体のパスを指定することもできるため、自己解決に頼らず固定パスで運用したい場合にも対応できる。

それでもnpmが見つからない場合のフェイルセーフとして、ビルド分岐そのものに入らず、PR作成だけに落とす処理を入れた。

if [ "$BUILD" = "1" ] && [ -d "$REPO/.../node_modules" ] && command -v npm >/dev/null 2>&1; then
  # ビルド実行
  ...
else
  # ビルド成功を機械で確認できていない状態で自動マージしてはいけない
  log "astro build 不可(node_modules 無し / npm 不在 / BUILD=0)→ 自動マージを見送り PR のみにする"
  AUTOMERGE="off"
fi

このジョブは「ビルド成功を機械的に確認できた変更だけを自動マージする」という条件のもとで無人稼働している。npmが見つからずビルドの成否を確認できない状態のまま自動マージへ進めば、壊れたビルドをそのまま本番に送りかねない。だからこの経路では自動マージ自体を止め、人間のレビュー待ちのPRとして残す方を選んだ。「直せないなら止まる」ではなく「直せないなら、直せない前提で安全側に倒す」という設計にしてある。

教訓

  • 常駐ジョブ(launchd・cron等)でNode系ツールを動かす場合、シェルの初期化スクリプト(nvm等)が実行される保証はない。ログインシェルの前提を無条件に持ち込まない
  • エラーメッセージの文言は、実際に失敗したコマンドの中身までは教えてくれない。「ビルドに失敗した」という報告を鵜呑みにせず、実行ログの生出力(npm: command not foundのような1行)まで遡って確認する価値がある
  • 自動化の一部が機械的に検証できない状態になったとき、無理に先へ進めるのではなく、その工程だけ人間の確認に戻す設計にしておくと、壊れた状態のまま自動マージされる事故を防げる

更新履歴

  • 2026-08-02: 初稿公開