Spring Bootを起動した瞬間に Migration checksum mismatch for migration version 3 とか Validate failed: Detected failed migration to version 5 みたいなエラーで固まった、という相談をよく受けます。どちらも Flyway が起動前のバリデーションで待ったをかけている状態ですが、原因も対処もまったく別物です。この記事では2つのエラーの見分け方から、ローカルでの flyway repair の試し方、本番で安易に repair を叩いてはいけない理由、flyway_schema_history テーブルを手動で直す手順までを症状ベースでまとめます。
2つのエラーメッセージを見分ける
まず出ているログをよく読みましょう。
ERROR: Migration checksum mismatch for migration version 3
-> Applied to database : -874034534
-> Resolved locally : 1198598178
ERROR: Validate failed: Detected failed migration to version 5 (add_index)
上が checksum mismatch です。適用済みマイグレーションファイルの内容と、DBに記録されているchecksumが一致していないという意味です。下は Detected failed migration で、flyway_schema_history に success = false の失敗レコードが残っている状態を指します。どちらもValidate処理が起動をブロックしているだけで原因は別なので、まずどちらのログが出ているかを確認してください。両方同時に出ることもあります。
checksumが変わってしまう典型的な原因
checksum mismatch の原因はだいたい次のどれかです。
- 一度適用済みのマイグレーションファイル(
V3__xxx.sqlなど)を後から編集してしまった - 改行コード(CRLF/LF)や文字コードがGitの設定やOSの違いで変わった
- CI/CDのビルド時にマイグレーションファイルが動的に生成・整形されていて、実行するたびに内容が微妙に揺れる
- Flywayやプラグインのバージョンを上げたらchecksumの計算対象が変わった
一番多いのは1番目です。「ちょっとタイポを直しただけ」でもchecksumは変わるので、適用済みファイルには絶対に触らないというルールがここで効いてきます。
まずflyway infoで現状を把握する
repairを叩く前に、flyway info で状況を客観的に確認しましょう。
./gradlew flywayInfo
# もしくは
mvn flyway:info
出力の State 列を見れば、どのバージョンが Success で、どれが Failed か、どれがchecksum不一致で扱われているかが分かります。本番環境で最初にやるべきなのはこの info だけで、書き込みを伴う repair や migrate はまだ実行しないでください。
ローカル環境でflyway repairを試す
原因がローカルの作業ミスだと分かっているなら、開発環境で repair を試してみます。
./gradlew flywayRepair
# flyway CLIの場合
flyway repair -url=jdbc:postgresql://localhost:5432/mydb -user=dev -password=dev
実行前後で flyway info を見比べると、checksum不一致だったバージョンが Success に変わり、失敗履歴が消えているのが確認できます。
flyway repairが実際にしていること
ここが誤解しやすいポイントです。repair は次の2つしかしていません。
flyway_schema_historyのchecksumを、今ローカルにあるマイグレーションファイルの内容に合わせて書き換えるsuccess = falseの失敗レコードを削除する
つまり 「ファイルとDB上の記録を一致させる」だけ で、実際のテーブル構造やデータが正しいかどうかは一切見ていません。checksumさえ揃えば起動は通ってしまうので、「直った」と勘違いしやすいコマンドです。
本番でrepairをいきなり実行してはいけない理由
本番でこの性質を忘れてrepairを叩くと危険です。たとえば V5__add_index.sql が途中まで実行されて失敗した場合、実際のテーブルにはインデックスが半分しか作られていないかもしれません。この状態でrepairすると履歴上は成功扱いになり、実体とのズレ(スキーマドリフト)がそのまま隠蔽されます。本番で本当に必要なのはchecksumを揃えることではなく、今のスキーマが意図通りかを実際に確認すること です。
本番復旧の手順
1. バックアップを取る
作業前に必ず flyway_schema_history 自体をバックアップします。
CREATE TABLE flyway_schema_history_backup_20260929 AS
SELECT * FROM flyway_schema_history;
可能であれば対象スキーマ全体のバックアップも取り、この時点の flyway info の出力もログとして残しておきましょう。
2. テーブル構造を確認する
主なカラムは次の通りです。
| カラム | 意味 |
|---|---|
installed_rank | 適用順序 |
version | マイグレーションのバージョン番号 |
checksum | ファイル内容から計算したハッシュ値 |
success | 適用に成功したかどうか |
installed_by | 実行したDBユーザー |
起動を止めているのは基本的に success = false の行です。
3. 手動でUPDATE/DELETEする
まず対象レコードを確認してから操作します。
SELECT * FROM flyway_schema_history WHERE success = false;
-- 実テーブルを目視確認したうえで、途中まで適用されていた変更を
-- 手動でDBに反映済みなら、失敗レコードを削除する
DELETE FROM flyway_schema_history WHERE version = '5' AND success = false;
-- ファイル内容は正しいのにchecksumだけがずれている場合
UPDATE flyway_schema_history
SET checksum = 1198598178
WHERE version = '3';
DELETEやUPDATEを実行したら、必ずアプリを起動する前に対象テーブル・カラムが本当に存在するかをSQLで目視確認してください。ここを飛ばすと、見せかけの整合性のまま本番に乗ってしまいます。
再発防止のルール
- 適用済みのマイグレーションファイルは絶対に編集しない。修正は新しいバージョンファイルを追加する
- CIパイプラインで
flyway validateを実行し、マージ前にchecksum不一致を検知する .gitattributesで改行コード・文字コードをリポジトリ全体で統一する
- name: Flyway Validate
run: ./gradlew flywayValidate
まとめ
checksum mismatch はファイルとDBの記録のズレ、Detected failed migration は失敗履歴の残存で、原因はまったく別です。repairは便利ですが、あくまでchecksumを合わせるだけのコマンドなので、本番では必ず flyway info での現状確認と実テーブルの目視確認をセットで行いましょう。Flywayの導入自体から見直したい場合は Spring BootでFlywayを使ったデータベースマイグレーション管理 を、起動失敗全般の切り分けは Spring Bootアプリが起動しない時の原因切り分けと対処法 もあわせて参考にしてください。Liquibaseとの比較で迷っている場合は Spring BootでLiquibaseを使ったデータベースマイグレーション管理 にまとめてあります。