Skip to content

URISyntaxException when resolving parent POM with unresolved ${revision} placeholder #7361

@XiaoSK

Description

@XiaoSK

Bug Description

When OpenRewrite parses a project whose transitive dependency tree includes a POM that was improperly published to a Maven repository (without flatten-maven-plugin), and that POM's <parent> version contains an unresolved CI-friendly placeholder like ${revision}, the entire resolution fails with a URISyntaxException or MavenDownloadingException.

The improperly published POM is only encountered through transitive dependency parent-chain resolution — the project being analyzed does not directly or meaningfully depend on the broken artifact. A single bad POM deep in the dependency graph should not cause the whole OpenRewrite run to fail. Maven itself handles this scenario gracefully, so OpenRewrite should ideally do the same — either resolve the version via metadata or skip the unresolvable parent and continue.

Error

Caused by: java.net.URISyntaxException: Illegal character in path at index 70:
  /home/user/.m2/repository/com/example/parent-project/${revision}/parent-project-${revision}.pom
    at java.net.URI$Parser.fail (URI.java:...)
    at java.net.URI$Parser.checkChars (URI.java:...)
    at java.net.URI$Parser.parseHierarchical (URI.java:...)
    at java.net.URI$Parser.parse (URI.java:...)
    at java.net.URI.<init> (URI.java:...)
    at java.net.URI.create (URI.java:...)
    at org.openrewrite.maven.internal.MavenPomDownloader.download (MavenPomDownloader.java:...)
    at org.openrewrite.maven.tree.ResolvedPom$Resolver.resolveParentPom (ResolvedPom.java:...)
    at org.openrewrite.maven.tree.ResolvedPom$Resolver.resolveParentPropertiesAndRepositoriesRecursively (ResolvedPom.java:...)
    at org.openrewrite.maven.tree.ResolvedPom$Resolver.resolveParentsRecursively (ResolvedPom.java:...)
    at org.openrewrite.maven.tree.ResolvedPom.resolveDependencies (ResolvedPom.java:...)
    at org.openrewrite.maven.tree.MavenResolutionResult.resolveDependencies (MavenResolutionResult.java:...)
    at org.openrewrite.maven.MavenParser.parseInputs (MavenParser.java:...)

Root Cause Analysis

The problematic POM

An artifact was improperly published to a Maven repository without the flatten-maven-plugin being applied. The published POM still contains raw CI-friendly placeholders:

<parent>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>${revision}</version>
</parent>
<artifactId>child-module</artifactId>
<version>${some.version}</version>

This can happen when:

  • The flatten-maven-plugin is declared in <pluginManagement> but not referenced in <plugins>, so it never actually executes.
  • A single version was accidentally deployed without the flatten plugin active.

In our case, out of 160+ published versions of the artifact, only one version (2.0.16) was improperly published with the raw ${revision} placeholder. All other versions were either properly flattened or had a hardcoded parent version. This single bad version, when encountered during transitive dependency resolution, causes the entire OpenRewrite run to fail.

Why resolution fails

When OpenRewrite encounters this POM as a transitive dependency and tries to resolve its parent chain, ResolvedPom.resolveParentPom() follows this flow:

  1. Raw GAV attempt: rawGav.getVersion() is "${revision}", so it tries downloader.download(rawGav, ...). This fails because the parent is not in the current project's reactor.

  2. Property resolution: getValues(rawGav) attempts to substitute ${revision} using the current POM's properties. This fails because revision is defined in the parent POM's own <properties> — a chicken-and-egg problem: we need the parent to resolve the version, but we need the version to find the parent.

  3. VersionRequirement resolution: VersionRequirement.fromVersion("${revision}", 0) creates a DirectRequirement because "${revision}" doesn't match any recognized version pattern (LATEST, RELEASE, or range). DirectRequirement.resolve() short-circuits and returns "${revision}" as-is, without ever querying repository metadata.

  4. Download attempt: downloader.download(gav.withVersion("${revision}"), ...) fails at MavenPomDownloader.download() with:

    MavenDownloadingException: Unable to download POM. Version contains unresolved property placeholder.
    

    Or, if that guard is bypassed, it reaches downloadMetadata() which calls URI.create() with ${revision} in the path, causing URISyntaxException since { and } are illegal URI characters.

Key code locations

  • ResolvedPom.resolveParentPom() — handles parent POM resolution
  • MavenPomDownloader.download() — checks for unresolved placeholders in version
  • MavenPomDownloader.downloadMetadata() — constructs URI for metadata download
  • VersionRequirement.fromVersion() — creates DirectRequirement for "${revision}"
  • VersionRequirement.cacheResolved()DirectRequirement returns version as-is without metadata lookup

How to Reproduce

  1. Publish a POM to a Maven repository without flattening it:

    <!-- parent-project/pom.xml -->
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>${revision}</version>
    <properties>
        <revision>1.0.0</revision>
    </properties>
    <!-- child-module/pom.xml (published without flatten) -->
    <parent>
        <groupId>com.example</groupId>
        <artifactId>parent-project</artifactId>
        <version>${revision}</version>
    </parent>
    <artifactId>child-module</artifactId>
  2. Create a separate project that depends on child-module:

    <dependency>
        <groupId>com.example</groupId>
        <artifactId>child-module</artifactId>
        <version>1.0.0</version>
    </dependency>
  3. Run OpenRewrite on the separate project — it will fail when resolving child-module's parent.

Expected Behavior

Since the improperly published artifact is only encountered through transitive dependency resolution and is not a direct concern of the project being analyzed, OpenRewrite should handle this gracefully rather than failing entirely. Possible approaches:

  1. Skip and continue: Catch the unresolvable ${revision} parent version as a MavenDownloadingException and let the existing exception-handling in resolveDependencies() accumulate it as a non-fatal warning, similar to how non-classpath artifact failures are already handled.
  2. Resolve via metadata: Query the parent artifact's repository metadata (which does not include the version in the URL path, avoiding the URI issue) and use the release or latest version to download the parent POM.

The key point is: a single improperly published POM that the project doesn't actually depend on should not break the entire OpenRewrite run.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions