Skip to content

render-dependencies breaks on Maven 3.6.3, the declared prerequisite #1682

Description

@slachiewicz

The POM declares

<prerequisites>
  <maven>3.6.3</maven>
</prerequisites>

but builds against <mavenVersion>3.9.16</mavenVersion>, and nothing in the build or in CI ever
checks the plugin against the version it claims to support. The two have drifted, and the drift
is already in released artifacts.

The defect

RenderDependenciesMojo line 241 uses the single-argument MojoExecutionException(Throwable)
constructor:

} catch (final IOException e) {
    throw new MojoExecutionException(e);
}

That constructor was added in Maven 3.9.0. It does not exist in 3.6.3:

$ javap -classpath maven-plugin-api-3.6.3.jar org.apache.maven.plugin.MojoExecutionException
  MojoExecutionException(java.lang.Object, java.lang.String, java.lang.String);
  MojoExecutionException(java.lang.String, java.lang.Exception);
  MojoExecutionException(java.lang.String, java.lang.Throwable);
  MojoExecutionException(java.lang.String);

$ javap -classpath maven-plugin-api-3.9.16.jar org.apache.maven.plugin.MojoExecutionException
  ... the four above, plus:
  MojoExecutionException(java.lang.Throwable);

The reference is in the shipped bytecode, not just the source — from the released
maven-dependency-plugin-3.11.0.jar:

$ javap -c -p org.apache.maven.plugins.dependency.fromDependencies.RenderDependenciesMojo
  34: invokespecial #298  // Method org/apache/maven/plugin/MojoExecutionException."<init>":(Ljava/lang/Throwable;)V

So on Maven 3.6.3 this throws NoSuchMethodError rather than the intended
MojoExecutionException. The path is narrow — it is the error branch, reached when
Files.createDirectories fails while writing the output file — but it turns a diagnosable
build failure into a linkage error.

RenderDependenciesMojo was added in 23186d4 (#1523) and is present in released
3.9.0, 3.10.0 and 3.11.0.

Reproducing

mvn -DmavenVersion=3.6.3 clean test-compile
[ERROR] .../RenderDependenciesMojo.java:[241,50] incompatible types:
        java.io.IOException cannot be converted to java.lang.String

(javac resolves the call to MojoExecutionException(String) once the Throwable overload is
absent, hence the message.) This is the only occurrence in src/main and src/test.

Why CI did not catch it

  • The verify matrix runs 3.10.0-rc-1 and 4.0.0-rc-6. The declared baseline is never exercised.
  • requireMavenVersion (3.9, inherited from the parent) constrains the Maven that builds the
    project, not the API level it compiles against.
  • <mavenVersion> is the only thing that expresses the API baseline, and it is free to move
    independently of <prerequisites>.

A class-level linkage check such as jdeps -verbose:class does not catch this — it resolves
classes, and MojoExecutionException is present in both versions. Only a member-level check,
i.e. compiling against the baseline, finds it.

Suggested resolution

Two separate things:

  1. Fix the call site — one line:
    throw new MojoExecutionException(e.getMessage(), e);
  2. Add a CI guard so the declared prerequisite is actually tested: a job that runs
    mvn -DmavenVersion=3.6.3 test-compile. Compiling main and test sources is enough to catch
    API drift and costs far less than running the suite.

Alternatively, if supporting 3.6.3 is no longer intended, raise <prerequisites> to match
<mavenVersion> — but that is a decision about the supported baseline, not a bug fix.

Drafted with Claude — please verify

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions