apache/maven · error · DuplicateMojoDescriptorException

Goal: {goal} already exists in the plugin descriptor for pre

Error message

Goal: {goal} already exists in the plugin descriptor for prefix: {goalPrefix}
Existing implementation is: {existingImplementation}
Conflicting implementation is: {newImplementation}

What it means

PluginDescriptor.addMojoDescriptor identifies mojos by role:roleHint, i.e. goalPrefix:goal. Adding a second MojoDescriptor with the same goal under the same prefix throws DuplicateMojoDescriptorException, reporting both the existing and the conflicting implementation classes.

Source

Thrown at compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptor.java:177

    public List<MojoDescriptor> getMojos() {
        return (List) getComponents();
    }

    public void addMojo(MojoDescriptor mojoDescriptor) throws DuplicateMojoDescriptorException {
        MojoDescriptor existing = null;
        // this relies heavily on the equals() and hashCode() for ComponentDescriptor,
        // which uses role:roleHint for identity...and roleHint == goalPrefix:goal.
        // role does not vary for Mojos.
        List<MojoDescriptor> mojos = getMojos();

        if (mojos != null && mojos.contains(mojoDescriptor)) {
            int indexOf = mojos.indexOf(mojoDescriptor);

            existing = mojos.get(indexOf);
        }

        if (existing != null) {
            throw new DuplicateMojoDescriptorException(
                    getGoalPrefix(),
                    mojoDescriptor.getGoal(),
                    existing.getImplementation(),
                    mojoDescriptor.getImplementation());
        } else {
            addComponentDescriptor(mojoDescriptor);
        }
    }

    public String getGroupId() {
        return groupId;
    }

    public void setGroupId(String groupId) {
        this.groupId = groupId;
    }

    public String getArtifactId() {

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Give each mojo a unique goal name (@Mojo(name = ...) or plugin.xml <goal>)
  2. Remove the stale duplicate class or plugin.xml entry
  3. If both mojos are legitimately needed, move one to a different plugin so the goalPrefix differs
  4. Regenerate the descriptor with maven-plugin-tools after any goal renames

Example fix

// before: two mojos, same goal name -> DuplicateMojoDescriptorException
@Mojo(name = "run") public class RunMojo extends AbstractMojo {}
@Mojo(name = "run") public class ExecMojo extends AbstractMojo {}

// after: unique goal per mojo under one goalPrefix
@Mojo(name = "run") public class RunMojo extends AbstractMojo {}
@Mojo(name = "exec") public class ExecMojo extends AbstractMojo {}
Defensive patterns

Strategy: validation

Validate before calling

// Guard programmatic descriptor assembly
if (pluginDescriptor.getMojo(mojoDescriptor.getGoal()) != null) {
    throw new IllegalArgumentException(
        "Goal already present: " + pluginDescriptor.getGoalPrefix() + ":" + mojoDescriptor.getGoal());
}
pluginDescriptor.addMojoDescriptor(mojoDescriptor);

Type guard

static boolean goalExists(PluginDescriptor pd, String goal) {
    return pd.getMojo(goal) != null;
}

Prevention

When it happens

Trigger: Two mojos in one plugin declaring the same goal name: two @Mojo(name = "run") classes, duplicate <goal> entries in plugin.xml, or repeated programmatic addMojoDescriptor calls.

Common situations: Splitting or copying a mojo class without changing @Mojo name; hand-edited plugin.xml; descriptor merging in tests or build tooling.

Related errors


AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21). Data as JSON: /api/errors/4789a66ce7dcda54. Report an issue: GitHub.