Webaff

公開 文責 Webaff 編集部

Netlifyのデプロイ失敗と本番チェックの誤検知を切り分けた記録

  • Netlify
  • 運用
  • Astro

2026-09-23、このブログ(Astro + Netlify)で、コードに問題がないのにデプロイと CI が立て続けに失敗しました。原因は Netlify 側の一時的な問題で、最終的に修正したのは CI のチェックだけです。ただ、切り分けの途中で「どこを見れば分かるか」「どこは分からないか」がはっきりしたので、手順として残します。

先に断っておくと、Netlify 内部エラーの根本原因までは分かりません。後半の「キャッシュ説」も仮説です。一方で、exit code: 4 の正体は、ダッシュボードのログで確認できました。

起きたこと

同じ日のうちに、性質の違う失敗が4つ重なりました。

順番 場所 表示 実際の原因
1 PR のプレビュー Failed to fetch environment variables(0秒で終了) Netlify 側の一時的な失敗
2 PR のプレビュー(再ビルド) 32秒後に exit code: 4 このときのログは未確認。同じ表示の別の例で内部エラーと確認(後述)
3 main の本番デプロイ Build failed due to an internal system error Netlify の内部エラー
4 GitHub Actions の live ジョブ webaff-blog.netlify.app が HTTP 500 3 の直後の一時的な 500(仮説)

どれも手元では再現しませんでした。npm run build は通り、同じコミットのブランチデプロイも成功していました。手元で再現しない、同じコミットが別の場所で通る、の2点が揃ったら、コードより環境を疑うという判断の目安になりました。

ログはどこまで取れるか

最初にやったのは、Netlify CLI でデプロイの状態を取ることです。

netlify api getDeploy --data '{"deploy_id":"<デプロイID>"}'

返ってくるのは state と error_message までです。Failed during stage 'building site': Build script returned non-zero exit code: 4 のようにどの段階で落ちたかは分かりますが、ビルドログの本文は API では取れません(getSiteBuild も同様)。exit code: 4 がどのコマンドの終了コードかは、この時点では分かりませんでした。

本番デプロイのほうは、ダッシュボードでログを開いて確認できました。段階の表示は次のとおりです。

  • Building: 成功
  • Deploying: 失敗(Internal error deploying)

ビルドは通っていて、成果物を配信に載せる段階で失敗しています。つまりコードの問題ではありません。段階別の表示が読めれば、切り分けはかなり進みます。

その後、別の PR のプレビューでまた exit code: 4 が出たときにダッシュボードのログを開くと、本番と同じ Build failed due to an internal system error: Build script returned non-zero exit code と Internal error deploying が並んでいました。API の error_message に出る exit code: 4 は、コマンドの失敗ではなく、この Netlify 内部エラーの表示です。手元のビルドとブランチデプロイが通る場合、exit code: 4 だけで自分のコードを疑う必要はありません。

再ビルドで直った

本番は同じ main のコミットで再ビルドしました。

netlify api createSiteBuild --data '{"site_id":"<サイトID>"}'

結果は ready で、/data/ に変更が反映されていることも本番の HTML で確認しました。PR のプレビューは、空コミットを push して再ビルドさせると通りました。履歴には残りますが、squash マージなら本流には入りません。

live ジョブだけが落ち続けた

本番が復旧しても、Actions の live ジョブは再実行後も HTTP 500 で落ちました。このジョブは本番 URL とは別に、webaff-blog.netlify.app が到達できることも確認します。

手元の curl はずっと 200 でした。ヘッダやユーザーエージェントを変えても再現しません。Actions では 3 回連続で 500、少し待って再実行すると成功しました。

疑ったのはキャッシュです。このサイトのトップページは SSR で、CDN に次の設定でキャッシュさせています。

Netlify-CDN-Cache-Control: public, durable, s-maxage=300, stale-while-revalidate=86400

デプロイが失敗していた間に、Actions の実行元に近い CDN ノードが 500 を返し、それが残った、という説明ができます。ただし 500 の中身を見ていないので、仮説のままです。場所によって結果が違った点だけは事実です。

直したこと

原因が一時的なものなら、チェック側で待てばよいはずです。live ジョブの本番確認には、期待状態になるまで再確認する仕組みがすでにありました。エイリアスの確認だけ、それがありませんでした。

  • 5xx のときだけ、既存の待機設定(既定で最大5分・20秒間隔)で再確認する
  • 4xx と 3xx は再試行しない(本当の設定ミスを隠さない)

動作は、500 を2回返してから 200 を返すローカルサーバーを立てて確認しました。2回の警告のあとに OK になります。本番に対する実行も OK でした。

この件で決めたこと

  • 手元で再現せず、同じコミットが別の場所で通るなら、コードを直す前にデプロイの段階別表示を見る
  • Netlify の API ではエラーメッセージまでしか取れないので、ログ本文はダッシュボードで見る(exit code: 4 は内部エラーの表示だった)
  • CI のチェックは、デプロイ直後の一時的な失敗を許容する作りにする(本当の失敗は待ち切ってから落ちる)

なお、独自ドメインの接続で同じ Netlify を扱った記録はNetlifyに独自ドメインを接続する手順、SSR ページを CDN に載せる前提になっている PV 集計の設計はNetlify BlobsでPVを数える設計とコスト対策にあります。

関連記事