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 workingpriority:criticalCrashes, loss of data, severe memory leak

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions