# How Maven Plugin Version Resolution and Prefix Resolution Work: A Deep Dive into the Apache Maven Source Code

> Discover how Maven resolves plugin versions and prefixes by scanning POMs and querying repositories. Understand the internal workings of Apache Maven for better build management.

- Repository: [The Apache Software Foundation/maven](https://github.com/apache/maven)
- Tags: deep-dive
- Published: 2026-07-05

---

**Maven resolves plugin prefixes by scanning the current POM for matching goal prefixes before falling back to repository metadata, and it resolves plugin versions by checking explicit POM declarations or querying remote repositories to select the latest compatible release.**

When you run a command like `mvn compiler:compile`, the Apache Maven build system must map the short prefix to fully-qualified coordinates and determine which version to execute. The `apache/maven` source code implements this through two dedicated resolver components—`DefaultPluginPrefixResolver` and `DefaultPluginVersionResolver`—that handle prefix mapping and version selection in distinct but coordinated phases. Understanding Maven plugin version resolution and prefix resolution is essential for debugging build failures and optimizing repository performance.

## How Maven Resolves Plugin Prefixes

The prefix resolution process translates a short goal prefix such as `compiler` into a complete `groupId:artifactId`. This logic lives in [`impl/maven-core/src/main/java/org/apache/maven/plugin/prefix/internal/DefaultPluginPrefixResolver.java`](https://github.com/apache/maven/blob/main/impl/maven-core/src/main/java/org/apache/maven/plugin/prefix/internal/DefaultPluginPrefixResolver.java).

### Scanning Project-Declared Plugins

Maven first inspects the current project's POM, checking the `<plugins>` and `<pluginManagement>` sections for candidates. In `DefaultPluginPrefixResolver.resolveFromProject` (lines 46–60), the resolver filters plugins whose `artifactId` contains the requested prefix. For each candidate, it loads the plugin descriptor via `BuildPluginManager.loadPlugin`. If the descriptor's `goalPrefix` matches the request, Maven returns the result wrapped in `DefaultPluginPrefixResult`. This project-local check ensures that explicitly declared plugins take precedence over remote metadata.

### Falling Back to Repository Metadata

If the prefix is not found in the POM, the resolver queries the repositories declared in the request. In `DefaultPluginPrefixResolver.resolveFromRepository` and `processResults` (lines 88–124 and 130–166), Maven iterates over candidate `groupId` values from the POM's `<pluginGroups>` or user-defined groups. For each group, it requests the [`maven-metadata.xml`](https://github.com/apache/maven/blob/main/maven-metadata.xml) file via `DefaultMetadata` and reads the content with `MetadataReader`. The metadata yields `<plugin>` entries containing both `artifactId` and `prefix`. The first entry whose `prefix` equals the requested value and whose `artifactId` is permitted by the candidate group set is returned as `DefaultPluginPrefixResult`. If no match is found anywhere, Maven throws a `NoPluginFoundForPrefixException`.

## How Maven Resolves Plugin Versions

Once the plugin coordinates are known, Maven determines the exact version to execute. This stage is governed by [`impl/maven-core/src/main/java/org/apache/maven/plugin/version/internal/DefaultPluginVersionResolver.java`](https://github.com/apache/maven/blob/main/impl/maven-core/src/main/java/org/apache/maven/plugin/version/internal/DefaultPluginVersionResolver.java).

### Project-Defined Versions

The resolver first checks the POM's `<plugins>` and `<pluginManagement>` sections for an explicit `<version>` element. In `DefaultPluginVersionResolver.resolveFromProject` (lines 73–78), if a version is declared directly in the project model, that value is returned immediately and no remote lookup occurs. This is the fastest and most deterministic path through Maven plugin version resolution.

### Repository Metadata and Compatibility Selection

When the POM omits a version, Maven queries remote repositories for the plugin's [`maven-metadata.xml`](https://github.com/apache/maven/blob/main/maven-metadata.xml). It creates a `DefaultMetadata` request for the plugin's `groupId/artifactId` pair and dispatches it to all configured repositories. The returned metadata is parsed via `MetadataReader.read` and merged into a unified `Versions` structure in `mergeMetadata`, tracking the *latest*, *release*, and all available versions alongside their timestamps and source repositories.

The selection logic in `DefaultPluginVersionResolver.selectVersion` (lines 75–112) follows a strict priority:

1. **Prefer the release version** (`releaseVersion`).
2. **If the release is incompatible**, fall back to the *latest* version.
3. **If neither works**, iterate over all versions—releases first, then snapshots—in descending order, picking the first candidate that passes the compatibility test.

The compatibility check, `isCompatible`, loads the plugin descriptor using `MavenPluginManager.getPluginDescriptor` and validates prerequisites through `pluginManager.checkPrerequisites`. If a version fails this validation, it is discarded and the next candidate is evaluated. The version resolver also caches successful lookups in a per-session `ConcurrentMap` keyed by `groupId`, `artifactId`, and repository list to avoid redundant remote fetches.

## The Complete Resolution Flow

When you invoke `mvn formatter:format`, Maven executes both resolvers in sequence before running the goal:

1. **Prefix resolution** — `DefaultPluginPrefixResolver` maps `formatter` to a fully-qualified plugin such as `org.apache.maven.plugins:maven-formatter-plugin`.
2. **Version resolution** — `DefaultPluginVersionResolver` checks the POM for an explicit version. If none exists, it queries repository metadata, prefers the latest compatible release, and selects a suitable version.

If you specify the full coordinate including the version, such as `mvn org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile`, Maven bypasses both the prefix and version resolution lookups entirely.

## Practical Examples

The following [`pom.xml`](https://github.com/apache/maven/blob/main/pom.xml) snippet pins the compiler plugin version, allowing the project-defined resolution path to succeed immediately:

```xml
<project>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.11.0</version>
        <configuration>
          <source>17</source>
          <target>17</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

```

To trigger prefix resolution from the command line, use the short form:

```bash
mvn compiler:compile

```

To bypass both prefix and version resolution, provide the fully-qualified coordinate with an explicit version:

```bash
mvn org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile

```

## Summary

- **Maven plugin prefix resolution** first scans the current POM's `<plugins>` and `<pluginManagement>` sections via `DefaultPluginPrefixResolver.resolveFromProject`, then falls back to repository [`maven-metadata.xml`](https://github.com/apache/maven/blob/main/maven-metadata.xml) through `resolveFromRepository`.
- If no prefix match is found in either location, Maven throws a `NoPluginFoundForPrefixException`.
- **Maven plugin version resolution** returns an explicitly declared POM version immediately via `DefaultPluginVersionResolver.resolveFromProject`.
- For undeclared versions, Maven queries remote metadata, merges results into a `Versions` object, and selects the newest compatible release in `DefaultPluginVersionResolver.selectVersion`.
- The `isCompatible` check validates each candidate against plugin prerequisites using `MavenPluginManager.getPluginDescriptor`.
- Successful version lookups are cached in a per-session `ConcurrentMap` to eliminate repeated remote metadata fetches.

## Frequently Asked Questions

### What happens when Maven cannot resolve a plugin prefix?

Maven throws a `NoPluginFoundForPrefixException` after exhausting both the project POM scan in `DefaultPluginPrefixResolver.resolveFromProject` and the repository metadata search in `resolveFromRepository`. This indicates that the prefix either does not exist in the configured plugin groups or is not declared in the current project.

### How does Maven choose between release and latest versions?

During repository-based version resolution, `DefaultPluginVersionResolver.selectVersion` first attempts to use the `releaseVersion` advertised in [`maven-metadata.xml`](https://github.com/apache/maven/blob/main/maven-metadata.xml). If `isCompatible` determines that the release is incompatible with the current Maven runtime, Maven falls back to the `latest` version, then iterates through remaining versions in descending order until it finds a compatible candidate.

### Can I skip version resolution by specifying the full plugin coordinate?

Yes. When you provide a fully-qualified command such as `mvn org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile`, Maven uses the supplied `groupId:artifactId:version` directly. This bypasses both `DefaultPluginPrefixResolver` and the repository lookup logic inside `DefaultPluginVersionResolver`.

### Where does Maven cache plugin version resolution results?

The default version resolver stores successful results in a per-session `ConcurrentMap` keyed by the plugin's `groupId`, `artifactId`, and the list of configured repositories. This cache prevents redundant remote metadata requests when the same plugin is referenced multiple times during a single build session.