Table of Contents#
- What is
ClassNotFoundException: FlywayCallback? - Why Does This Error Happen?
- How to Fix the Error
- Troubleshooting Common Issues
- Conclusion
- References
What is ClassNotFoundException: FlywayCallback?#
A ClassNotFoundException occurs when the Java Virtual Machine (JVM) tries to load a class but cannot find its definition in the classpath. In this case, the missing class is org.flywaydb.core.api.callback.FlywayCallback—an interface used in Flyway, a popular database migration tool, to define custom callbacks (e.g., logic to run before/after migrations).
If your Spring Boot application throws this error, it means:
- Your code references
FlywayCallbackexplicitly (e.g., a custom callback class implementing it). - The JVM cannot locate this class in the runtime classpath.
Why Does This Error Happen?#
To resolve the error, we first need to understand why the FlywayCallback class is missing. Let’s break down the most common causes:
1. Flyway Version Incompatibility#
Flyway frequently updates its API, and version 9.0.0 (released in 2022) introduced breaking changes to its callback system. Prior to Flyway 9, the callback interface was named FlywayCallback and lived in the package org.flywaydb.core.api.callback.
If your application:
- Uses Flyway 9.0.0 or later, and
- Contains code that references
FlywayCallback(from pre-9 versions),
the JVM will throw ClassNotFoundException because FlywayCallback was removed in Flyway 9.
2. Deprecated API: Flyway 9+ Renamed the Callback Interface#
In Flyway 9, the FlywayCallback interface was renamed to Callback (same package: org.flywaydb.core.api.callback). This was part of a broader effort to simplify the API and align with modern Java conventions.
Example:
- Pre-Flyway 9:
public class MyCallback implements FlywayCallback { ... } - Flyway 9+:
public class MyCallback implements Callback { ... }
If you haven’t updated your callback classes to use Callback instead of FlywayCallback, the old interface name will no longer resolve.
3. Dependency Misconfiguration#
Spring Boot uses "starters" to auto-manage dependencies. The spring-boot-starter-flyway starter includes Flyway by default, but version mismatches can still occur:
- Explicit Flyway Version Override: If you manually specify an older Flyway version in your
pom.xml/build.gradlebut your code usesCallback(Flyway 9+), or vice versa. - Transitive Dependencies: A third-party library might pull in an outdated Flyway version, conflicting with your application’s expected version.
How to Fix the Error#
Now that we understand the root causes, let’s walk through fixing the error step-by-step.
Step 1: Identify Your Flyway Version#
First, confirm which Flyway version your application is using. This determines whether FlywayCallback (pre-9) or Callback (9+) is correct.
For Maven:#
Run this command to list all dependencies and their versions:
mvn dependency:tree | grep flyway For Gradle:#
Run:
./gradlew dependencies | grep flyway Look for a line like:
org.flywaydb:flyway-core:9.16.3 # Flyway 9+ (uses Callback)
# OR
org.flywaydb:flyway-core:8.5.13 # Pre-Flyway 9 (uses FlywayCallback)
Step 2: Update Callback Code to Use the New API#
If you’re on Flyway 9+, replace all references to FlywayCallback with Callback. Here’s how:
Example: Old Callback (Pre-Flyway 9)#
import org.flywaydb.core.api.callback.FlywayCallback;
import org.flywaydb.core.api.callback.Context;
import org.flywaydb.core.api.callback.Event;
public class LegacyFlywayCallback implements FlywayCallback {
@Override
public void beforeMigrate(Context context) {
System.out.println("Running before migrations...");
}
// Other methods (beforeClean, afterMigrate, etc.)
} Updated Callback (Flyway 9+)#
Flyway 9+ simplifies the callback interface. The Callback interface has a single method: handleEvent(Event event, Context context). Use the Event enum to check when to trigger logic:
import org.flywaydb.core.api.callback.Callback; // New interface name
import org.flywaydb.core.api.callback.Context;
import org.flywaydb.core.api.callback.Event;
public class ModernFlywayCallback implements Callback {
@Override
public void handleEvent(Event event, Context context) {
if (event == Event.BEFORE_MIGRATE) { // Check the event type
System.out.println("Running before migrations...");
}
// Handle other events (e.g., AFTER_MIGRATE, BEFORE_CLEAN) as needed
}
// Optional: Override to specify if the callback applies to all databases
@Override
public boolean supports(Event event, Context context) {
return true; // Applies to all events/databases
}
} Step 3: Adjust Dependencies (If Needed)#
If your Flyway version is misaligned with your code, fix your build configuration to ensure consistency.
Use Spring Boot’s Managed Dependency (Recommended)#
Spring Boot’s spring-boot-starter-flyway starter automatically includes a compatible Flyway version. Avoid explicitly defining flyway-core unless necessary:
Maven (pom.xml):
<dependencies>
<!-- Let Spring Boot manage Flyway version -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-flyway</artifactId>
</dependency>
</dependencies> Gradle (build.gradle):
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-flyway'
} Override Flyway Version (If Required)#
If you need a specific Flyway version (e.g., to downgrade to 8.x), explicitly set it in your build file. Ensure your code uses FlywayCallback (not Callback) in this case:
Maven:
<properties>
<!-- Force Flyway 8.5.13 (pre-9) -->
<flyway.version>8.5.13</flyway.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-flyway</artifactId>
</dependency>
</dependencies> Step 4: Verify the Fix#
After updating your code and dependencies:
- Rebuild your application (e.g.,
mvn clean packageor./gradlew build). - Run the application. The
ClassNotFoundExceptionshould no longer occur. - Test your custom callbacks to ensure they trigger as expected (e.g., check logs for "Running before migrations...").
Troubleshooting Common Issues#
Issue 1: Multiple Callback Implementations#
If you have multiple callback classes, ensure all of them use Callback (Flyway 9+) or FlywayCallback (pre-9). Mixing old and new interfaces will cause errors.
Issue 2: Transitive Dependency Conflicts#
If a third-party library pulls in an outdated Flyway version, use your build tool to enforce the correct version:
Maven: Use <dependencyManagement> to override transitive versions:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>9.16.3</version> <!-- Enforce Flyway 9+ -->
</dependency>
</dependencies>
</dependencyManagement> Gradle: Use resolutionStrategy in build.gradle:
configurations.all {
resolutionStrategy {
force 'org.flywaydb:flyway-core:9.16.3'
}
} Issue 3: Spring Boot Auto-Configuration#
Spring Boot auto-detects Flyway callbacks in the classpath. If your callback isn’t triggering, ensure:
- It’s annotated with
@Component(so Spring detects it). - The
supports()method (in Flyway 9+) returnstruefor the events you care about.
Conclusion#
The ClassNotFoundException: FlywayCallback error in Spring Boot is almost always caused by version incompatibility between your code and Flyway. Flyway 9+ renamed FlywayCallback to Callback, so updating your callback classes and aligning dependencies will resolve the issue.
By following the steps above—identifying your Flyway version, updating callback code, and ensuring dependency consistency—you’ll have your application running smoothly again.
References#
- Flyway 9.0.0 Release Notes (breaking changes to callbacks)
- Flyway Callback Documentation
- Spring Boot Flyway Starter
- Maven Dependency Tree
- Gradle Dependency Management