Skip to content

Maven 4 (rc5) fails in maven-install-plugin due to BUILD_CONSUMER disabling profile activation for locally-resolved parent POMs #11798

@DavidTavoularis

Description

@DavidTavoularis

Affected version

4.0.0-rc5

Bug description

Note that I asked Claude Code to help with the analysis and the fix.

This is a different bug from #11767, discovered while testing the fixes from that ticket. The #11767 fix (PR #11768) correctly passes repositories and profiles to the consumer POM builder, but a separate code path still causes failures when parent POMs define properties inside profiles with property-based activation.

$ mvn clean install
[...]
[INFO] --- install:3.1.3:install (default-install) @ xxx ---
[INFO] --------------------------------------------------------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] --------------------------------------------------------------------------------------------------------------------------
[...]
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-install-plugin:3.1.3:install (default-install) on project xxx: Execution default-install of goal org.apache.maven.plugins:maven-install-plugin:3.1.3:install failed: Invalid Version Range Request: org.springframework:spring-framework-bom:pom:${spring.version} [...]

The property ${spring.version} is defined in a great-grandparent POM (lineup) inside a profile with property-based activation:

<!-- lineup pom.xml -->
  <profiles>
      <profile>
          <id>spring-old</id>
          <activation>
              <property>
                  <name>!explicitelyDeactivated</name>
              </property>
          </activation>
          <properties>
              <spring.version>5.3.39</spring.version>
          </properties>
      </profile>
  </profiles>

A grandparent POM uses this property in a BOM import:

  <dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-framework-bom</artifactId>
              <version>${spring.version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
  </dependencyManagement>

During BUILD_PROJECT, profiles are properly activated and the property resolves to 5.3.39. During BUILD_CONSUMER (install phase), the property is null and the BOM import fails with Invalid Version Range Request.

Root cause
The bug is in DefaultModelBuilder.readParentLocally() (line ~1151 in DefaultModelBuilder.java).
When the consumer POM is built (BUILD_CONSUMER), readParentLocally() finds the parent POM via resolveReactorModel() (because the parent was loaded into mappedSources during the earlier BUILD_PROJECT phase). It then calls:

  // Line ~1151 - DefaultModelBuilder.java - readParentLocally()
  ModelBuilderSessionState derived = derive(candidateSource);

derive(candidateSource) calls derive(ModelBuilderRequest.build(request, source)) which preserves the BUILD_CONSUMER request type. Since isBuildRequestWithActivation() returns false for BUILD_CONSUMER, POM profile activation is skipped for all parent POMs in the chain:

  boolean isBuildRequestWithActivation() {
      return request.getRequestType() != ModelBuilderRequest.RequestType.BUILD_CONSUMER;
  }

In contrast, resolveAndReadParentExternally() (used when the parent is NOT in the reactor) explicitly creates a CONSUMER_PARENT request:

  // Line ~1284 - DefaultModelBuilder.java - resolveAndReadParentExternally()
  ModelBuilderRequest lenientRequest = ModelBuilderRequest.builder(request)
          .requestType(ModelBuilderRequest.RequestType.CONSUMER_PARENT)
          .source(modelSource)
          .build();

CONSUMER_PARENT allows profile activation, so parent POMs resolved externally work correctly. Only parent POMs resolved locally (via reactor/mappedSources) are affected.

Fix
In readParentLocally(), when the current request is BUILD_CONSUMER, derive the parent session with CONSUMER_PARENT type — consistent with how resolveAndReadParentExternally() handles it:

  ModelBuilderSessionState derived;
  if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_CONSUMER) {
      ModelBuilderRequest parentRequest = ModelBuilderRequest.builder(request)
              .requestType(ModelBuilderRequest.RequestType.CONSUMER_PARENT)
              .source(candidateSource)
              .build();
      derived = derive(parentRequest);
  } else {
      derived = derive(candidateSource);
  }

Claude Code created the following unit test (which fails without the fix):
Test: DefaultModelBuilderTest.testBuildConsumerResolvesParentProfileProperties (maven-impl)

  • Builds a parent POM with BUILD_PROJECT to populate the reactor (mappedSources) — the parent defines managed.version=1.2.3 inside a profile with property-based activation (!skipDefaultVersions)
  • Builds the child POM with BUILD_CONSUMER on the same session — the parent is found via resolveReactorModel() in readParentLocally()
  • Asserts the effective model has managed.version=1.2.3 (from the parent's profile)
  • Asserts the managed dependency version is interpolated to 1.2.3, not ${managed.version}
    Without the fix: AssertionFailedError: expected: <1.2.3> but was: <null>

I was also able to confirm that when implementing the fix on top of 4.0-rc5, my mvn clean install passed.

I am now preparing a Pull Request.

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