Introduction

Flyway calculates a checksum for each migration file and stores it in the flyway_schema_history table. When a migration file is modified after being applied (even a whitespace change), the checksum no longer matches and Flyway refuses to run any further migrations. This is a safety mechanism to prevent accidental changes to already-applied migrations, but it commonly causes issues when developers edit migration files for formatting, when Git line-ending conversions alter files, or when migration files are committed from different machines with different editors.

Symptoms

``` org.flywaydb.core.internal.command.DbMigrate$FlywayMigrateException: Migration V002__create_users_table.sql changed checksum ----------------- Script checksum: Calculated: 1234567890 Recorded: 0987654321

Flyway attempted to validate the migration script against the recorded checksum, but they did not match. ```

Application fails to start:

``` ******* APPLICATION FAILED TO START *******

Description:

Flyway migration validation failed: Migration V002__create_users_table.sql has changed since it was applied. ```

Common Causes

  • Editing an already-applied migration file: Changing SQL content, comments, or even whitespace
  • Git line-ending conversion: CRLF vs LF changes the file content and checksum
  • Different Flyway versions: Flyway 9 and Flyway 10 may calculate checksums differently
  • Migration file committed from different machine: Different editor adds trailing newline or BOM
  • Manual database changes: Someone modified the database schema outside of Flyway
  • Merge conflict resolution: Git merge changes the migration file content

Step-by-Step Fix

Step 1: Use Flyway repair to fix the checksum

If the migration content is intentionally the same (only whitespace changed):

```bash # With Flyway CLI flyway repair

# With Maven mvn flyway:repair

# With Gradle gradle flywayRepair

# With Spring Boot # Set this property to enable repair on startup (use with caution!) spring.flyway.repair-on-migrate=true ```

This updates the checksum in flyway_schema_history to match the current file.

Step 2: Use baseline for existing databases

If the database was modified outside of Flyway:

```sql -- Check current state SELECT * FROM flyway_schema_history ORDER BY installed_rank;

-- Baseline the database at the current state flyway baseline -baselineVersion=001 -baselineDescription="Baseline" ```

Or in Spring Boot:

yaml
spring:
  flyway:
    baseline-on-migrate: true
    baseline-version: "001"

Step 3: Use repeatable migrations for changing scripts

For scripts that change frequently (stored procedures, views, functions), use repeatable migrations:

sql
-- db/migration/R__update_user_view.sql
CREATE OR REPLACE VIEW user_summary AS
SELECT u.id, u.name, u.email,
       COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name, u.email;

Repeatable migrations (prefixed with R__) are re-executed every time their checksum changes, without causing a mismatch error.

Step 4: Prevent future checksum changes

Add a pre-commit hook:

```bash #!/bin/bash # .pre-commit-hooks/check-migration-files.sh # Prevent changes to already-applied migration files APPLIED_MIGRATIONS=$(find src/main/resources/db/migration -name "V*.sql" | sort)

for file in $APPLIED_MIGRATIONS; do if git diff --cached --name-only | grep -q "$(basename $file)"; then echo "ERROR: Cannot modify already-applied migration: $(basename $file)" echo "Create a new migration file instead." exit 1 fi done ```

Prevention

  • Never edit a migration file once it has been applied to any environment
  • Create a new migration file for any schema changes, even to fix mistakes
  • Use R__ repeatable migrations for views, stored procedures, and functions
  • Configure Git with *.sql text eol=lf in .gitattributes to prevent line-ending changes
  • Add flyway validate to your CI pipeline to catch checksum issues before deployment
  • Document the migration workflow in your team wiki: "create new file, never edit existing"