apache/maven · warning

artifact '{}' already attached, replacing previous instance

Error message

artifact '{}' already attached, replacing previous instance

What it means

MavenProject.addAttachedArtifact() was called with coordinates (groupId:artifactId:version:classifier:type) that already exist in the attached-artifacts list. Since 3.0.x the method replaces the previous instance instead of throwing DuplicateArtifactAttachmentException (kept only for binary compatibility); the old file reference is discarded. The method is deprecated in favor of MavenProjectHelper. The warning fires on every replacement, which is how plugins like shade intentionally retarget an attached artifact's file path.

Source

Thrown at impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java:1095

        return this.injectedProfileIds;
    }

    /**
     * Add or replace an artifact. This method is now deprecated. Use the @{MavenProjectHelper} to attach artifacts to a
     * project. In spite of the 'throws' declaration on this API, this method has never thrown an exception since Maven
     * 3.0.x. Historically, it logged and ignored a second addition of the same g/a/v/c/t. Now it replaces the file for
     * the artifact, so that plugins (e.g. shade) can change the pathname of the file for a particular set of
     * coordinates.
     *
     * @param artifact the artifact to add or replace.
     * @deprecated Please use {@link MavenProjectHelper}
     * @throws DuplicateArtifactAttachmentException will never happen but leave it for backward compatibility
     */
    public void addAttachedArtifact(Artifact artifact) throws DuplicateArtifactAttachmentException {
        // if already there we remove it and add again
        int index = attachedArtifacts.indexOf(artifact);
        if (index >= 0) {
            LOGGER.warn("artifact '{}' already attached, replacing previous instance", artifact);
            attachedArtifacts.set(index, artifact);
        } else {
            attachedArtifacts.add(artifact);
        }
    }

    /**
     * Returns a read-only list of the attached artifacts to this project.
     *
     * @return the attached artifacts of this project
     */
    public List<Artifact> getAttachedArtifacts() {
        if (attachedArtifacts == null) {
            attachedArtifacts = new ArrayList<>();
        }
        return Collections.unmodifiableList(attachedArtifacts);
    }

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Inspect the effective lifecycle (mvn -X or mvn help:effective-pom) for two executions binding plugins that attach artifacts with the same classifier
  2. Give each attachment a distinct <classifier> so both files are kept
  3. If the replacement is intended (shade retargeting), treat the warning as expected noise or raise the logger threshold for org.apache.maven.project.MavenProject
  4. In custom mojos, switch to MavenProjectHelper.attachArtifact(...) and guard with a getAttachedArtifacts() lookup before attaching

Example fix

<!-- before: two executions attach artifacts with no classifier, second replaces first -->
<plugin>
  <artifactId>maven-shade-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals><goal>shade</goal></goals>
    </execution>
  </executions>
</plugin>

<!-- after: shaded jar gets its own classifier, both artifacts installed -->
<plugin>
  <artifactId>maven-shade-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals><goal>shade</goal></goals>
      <configuration>
        <shadedArtifactAttached>true</shadedArtifactAttached>
        <shadedClassifierName>shaded</shadedClassifierName>
      </configuration>
    </execution>
  </executions>
</plugin>
Defensive patterns

Strategy: validation

Validate before calling

// In a mojo: only attach when the coordinates are not already present
import org.apache.maven.artifact.Artifact;

boolean alreadyAttached(MavenProject project, Artifact artifact) {
    return project.getAttachedArtifacts().stream().anyMatch(a ->
            a.getArtifactId().equals(artifact.getArtifactId())
            && a.getGroupId().equals(artifact.getGroupId())
            && a.getVersion().equals(artifact.getVersion())
            && java.util.Objects.equals(a.getClassifier(), artifact.getClassifier())
            && a.getType().equals(artifact.getType()));
}
if (!alreadyAttached(project, artifact)) {
    project.addAttachedArtifact(artifact); // prefer MavenProjectHelper in production code
}

Prevention

When it happens

Trigger: A mojo calls project.addAttachedArtifact(artifact) or MavenProjectHelper.attachArtifact(...) twice with identical g/a/v/c/t but different files: two plugin executions in the same lifecycle run both attaching (e.g. shade + source plugin attaching the same classifier), or a plugin re-running on a cached MavenProject instance.

Common situations: shade plugin replacing the main artifact's file after another plugin already attached it; source/javadoc plugins executing twice due to duplicated execution bindings; custom mojos attaching test-jar and a classified jar with an empty classifier by mistake; incremental builds reusing a project instance.

Related errors


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