Skip to content

Dependency-reduced POM drops <exclusion> on classifier-distinct duplicate dependencies (Maven 4) #819

Description

@ascheman

Affected versions

  • maven-shade-plugin master (a6c103f5).
  • Building under Maven 4.0.0-SNAPSHOT (commit 0a61d8348b, 2026-06-27 — shortly before the 4.0.0-rc-6 cut).
  • Does NOT reproduce under Maven 3.9.16 (see below) — this is Maven-4-specific.

Symptom (not a build failure)

The plugin compiles, packages, and passes all unit tests against 4.0.0-SNAPSHOT; only 2 of 84 integration tests fail, and both fail on the content of the generated dependency-reduced-pom.xml — not a build/compile error: dep-reduced-pom-exclusions and MSHADE-467_parallel-dependency-reduced-pom.

When a shaded dependency has a transitive dependency that occurs as a classifier-distinct duplicate (b:0.2 and b:0.2:alt) and that dependency carries an <exclusion>, the generated dependency-reduced POM keeps the <exclusion> on only ONE of the two variants; the classifier variant ends up with none.

Maven 3 vs Maven 4

  • Maven 3.9.16: both ITs PASS — both b variants carry the exclusion.
  • Maven 4.0.0-SNAPSHOT: both fail — b:0.2:alt has 0 exclusions (expected 1); MSHADE-467 exclusion count 3 vs 5.

Root cause

ShadeMojo.updateExcludesInDeps() discovers each direct dependency's transitive set via a single repositorySystem.collectDependencies(...) (~ShadeMojo.java:1302) and walks node.getChildren(). Under Maven 4 that collected graph is conflict-resolved with the second c omitted (omitted for duplicate) under b:0.2:alt, so shade never sees c as a child of the classifier variant and applies the exclusion to b:0.2 only. Shade's own dependency keying (getId() = groupId:artifactId:type:classifier) is classifier-aware and not at fault.

Suggested fix — two alternatives (either resolves it; both keep Maven 3 green)

  1. Collect with resolver verbose conflict resolution (aether.conflictResolver.verbose=true) so the omitted-duplicate node is retained as a marker (shade only needs its groupId:artifactId); the existing walk then finds c under both b variants. or
  2. Collect each direct dependency independently (its own CollectRequest) so the duplicate never arises, mapping subtrees back via the existing classifier-aware getId.

Regression coverage already exists: the two ITs above (red on Maven 4, green on Maven 3).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions