apache/maven · error · PluginExecutionException
A type incompatibility occurred while executing ${mojoDescri
Error message
A type incompatibility occurred while executing ${mojoDescriptor.getId()}: ${e.getMessage()} What it means
A ClassCastException escaped a mojo execution. In Maven this almost always means class identity broke across classloaders: the same class name is loaded both from the plugin realm and from Maven core (or another realm), so a JVM-level cast between 'identical' types fails. Maven dumps the plugin realm and fails the build with PluginExecutionException.
Source
Thrown at impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java:195
} catch (LinkageError e) {
mojoExecutionListener.afterExecutionFailure(
new MojoExecutionEvent(session, project, mojoExecution, mojo, e));
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
PrintStream ps = new PrintStream(os);
ps.println("An API incompatibility was encountered while executing " + mojoDescriptor.getId() + ": "
+ e.getClass().getName() + ": " + e.getMessage());
pluginRealm.display(ps);
Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e);
throw new PluginExecutionException(mojoExecution, project, wrapper);
} catch (ClassCastException e) {
mojoExecutionListener.afterExecutionFailure(
new MojoExecutionEvent(session, project, mojoExecution, mojo, e));
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
PrintStream ps = new PrintStream(os);
ps.println("A type incompatibility occurred while executing " + mojoDescriptor.getId() + ": "
+ e.getMessage());
pluginRealm.display(ps);
throw new PluginExecutionException(mojoExecution, project, os.toString(), e);
} catch (RuntimeException e) {
mojoExecutionListener.afterExecutionFailure(
new MojoExecutionEvent(session, project, mojoExecution, mojo, e));
throw e;
} finally {
mavenPluginManager.releaseMojo(mojo, mojoExecution);
scope.exit();
Thread.currentThread().setContextClassLoader(oldClassLoader);
legacySupport.setSession(oldSession);
}
}
/**
* TODO pluginDescriptor classRealm and artifacts are set as a side effect of this
* call, which is not nice.
* @throws PluginResolutionException
*/
@OverrideView on GitHub (pinned to e4093d4e12)
Solutions
- Read the cast message: 'class X cannot be cast to class X' with two different ClassLoaders named in the detail confirms a realm conflict.
- Find the duplicate artifact in the realm dump and exclude it from the plugin, or align its version with the one Maven core provides.
- If you own the plugin, depend on the shared API with provided scope instead of bundling it.
- Upgrade to a plugin version tested against your Maven release.
Example fix
<!-- before: plugin drags its own commons-cli 1.3, clashing with another copy in the realm -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<dependencies>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
</plugin>
<!-- after: drop the redundant dependency so a single copy loads -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
</plugin> Defensive patterns
Strategy: type-guard
Type guard
static boolean realmCompatible(Object value, Class<?> target) {
return value == null
|| (target.getClassLoader() == value.getClass().getClassLoader() && target.isInstance(value));
} Try / catch
catch (ClassCastException e) {
if (e.getMessage() != null && e.getMessage().contains("cannot be cast to")) {
// the detail line names both ClassLoaders: a cross-realm leak, fix dependencies
log.error("classloader clash: {}", e.getMessage());
}
throw e;
} Prevention
- Do not bundle libraries Maven core already provides into your plugin
- Use provided scope for maven-plugin-api and shared core libraries
- Run dependency:tree and check for overlapping classes before shipping a plugin
When it happens
Trigger: A plugin depends on a library that Maven core also exports at a different version, and an object created in one classloader is passed to code that casts it in the other. Also: two plugin dependencies ship the same classes in different artifacts and class resolution picks the incompatible copy.
Common situations: Plugins bundling guava/commons-*/plexus-utils versions that clash with Maven's own; shaded or relocated jars duplicating class names; extensions and plugins sharing types across realms.
Related errors
- A required class was missing while executing {}: {}
- An API incompatibility was encountered while executing ${moj
- No plugin descriptor found at ${pluginDescriptorLocation}
- Failed to parse plugin descriptor for ${plugin.getId()} (${p
- Invalid plugin descriptor for ${plugin.getId()} (${pluginFil
AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21).
Data as JSON: /api/errors/85aa2689b9c4bd60.
Report an issue: GitHub.