Published on

Maven Dependency Cache Troubleshooting: Use -U Before Deleting .m2

Authors
  • Name
    Code Mill Hub Editorial
    Twitter

A Maven build can fail on one machine and pass on another even when both machines use the same commit. The usual suspect is ~/.m2/repository, but deleting that entire directory is rarely the best first response. It discards every cached artifact, increases load on remote repositories, and can hide the actual difference between environments.

The safer approach is to identify which layer is stale:

  1. the Maven runtime or settings;
  2. remote repository metadata;
  3. a cached failed download;
  4. a snapshot artifact;
  5. one corrupt or incorrect dependency;
  6. the project's dependency graph.

Maven provides a different tool for each case. This guide turns the familiar mvn clean install -U command into a repeatable troubleshooting sequence rather than a universal cache reset.

TL;DR

  • Run the project's Maven Wrapper, not an arbitrary system Maven installation.
  • Capture help:effective-settings and help:effective-pom before changing the cache.
  • -U forces Maven to check for missing releases and updated snapshots; it does not mean “redownload everything.”
  • Use dependency:purge-local-repository or remove one artifact directory when a specific dependency is suspect.
  • Compare the resolved dependency tree in local and CI environments.
  • Keep credentials out of logs and never commit a real settings.xml.

Start with the environment, not the cache

Two Maven invocations may see different repositories, mirrors, profiles, credentials, proxies, Java runtimes, or Maven versions. Establish the runtime first.

./mvnw --version
java -version

On Windows:

.\mvnw.cmd --version
java -version

The Maven Wrapper pins the Maven distribution in .mvn/wrapper/maven-wrapper.properties. It reduces “works on my machine” differences because contributors and CI use the version selected by the project.

If the project has no wrapper yet, Apache Maven documents this setup command:

mvn wrapper:wrapper

Review and commit the generated wrapper files according to the project's supply-chain policy. Apache Maven also supports distributionSha256Sum and wrapperSha256Sum in maven-wrapper.properties to verify downloaded wrapper components.

Capture the effective configuration

The POM visible in the repository is not the whole build model. Parent POMs, active profiles, user settings, global settings, mirrors, and command-line properties all contribute to the effective configuration.

./mvnw help:active-profiles
./mvnw help:effective-settings -Doutput=target/effective-settings.xml
./mvnw help:effective-pom -Doutput=target/effective-pom.xml

Treat target/effective-settings.xml as sensitive. It can reveal internal repository hosts or usernames even when passwords are masked. Keep it in a local diagnostic directory, do not attach it to a public issue, and do not commit it.

Compare local and CI results for:

  • the Maven and Java versions;
  • active profiles;
  • mirror selection;
  • repository IDs and URLs;
  • snapshot and release policies;
  • parent and dependency versions after property interpolation.

This step often explains the failure without touching the cache.

What -U actually changes

For Maven 3, the official CLI describes -U as forcing a check for missing releases and updated snapshots on remote repositories.

./mvnw -U clean verify

That makes -U appropriate when:

  • a SNAPSHOT was redeployed;
  • Maven previously failed to find an artifact and cached that result;
  • repository metadata may have changed;
  • a release became available after the last failed attempt.

It is not a reliable fix for:

  • a wrong version declared in the POM;
  • a mirror that points to the wrong repository;
  • missing repository credentials;
  • a dependency conflict;
  • a plugin execution failure;
  • a corrupt artifact that Maven still considers valid.

Run verify, not install, unless another local project actually consumes the installed artifact. verify exercises the lifecycle through tests and verification without adding the current project to the local repository.

For non-interactive CI output:

./mvnw -B -ntp -U clean verify

-B enables batch mode and -ntp suppresses transfer progress. Do not add -U to every build automatically: it increases remote metadata checks and makes a build more dependent on repository availability. Use it deliberately or define an update policy that matches the repository's role.

Understand repository update policies

Maven repository policies can specify when releases or snapshots are checked:

<repository>
  <id>example-releases</id>
  <url>https://repo.example.com/releases</url>
  <releases>
    <enabled>true</enabled>
    <updatePolicy>daily</updatePolicy>
    <checksumPolicy>fail</checksumPolicy>
  </releases>
  <snapshots>
    <enabled>false</enabled>
  </snapshots>
</repository>

The supported updatePolicy values include always, daily, interval:X, and never. Use snapshots only in a repository intended for them. For releases, immutable coordinates are the safest contract: publishing different bytes under the same release version makes caches inconsistent by design.

Prefer checksumPolicy=fail where the repository supports reliable checksums. A checksum failure is evidence to investigate, not a prompt to disable validation.

Inspect the dependency graph

If the artifact resolves but the wrong library reaches the runtime, refreshing metadata will not solve the graph.

./mvnw dependency:tree -Dverbose

Limit a large tree to the relevant group or artifact:

./mvnw dependency:tree \
  -Dincludes=org.example:problematic-library

Capture a machine-readable tree for comparison:

./mvnw dependency:tree \
  -DoutputType=json \
  -DoutputFile=target/dependency-tree.json

Check whether local and CI builds select the same version and path. Common causes of divergence include an activated profile, a different parent POM, a version range, or a locally installed artifact that does not exist in the remote repository.

If a multi-module build fails only when invoked from the root, verify the reactor selection too:

./mvnw -pl :affected-module -am clean verify

-pl selects a project and -am also builds its required reactor dependencies. This is safer than testing the module alone against potentially stale locally installed siblings.

Purge only the suspect dependency

When one dependency is known to be wrong or corrupt, use the Maven Dependency Plugin instead of deleting all of ~/.m2/repository.

./mvnw dependency:purge-local-repository \
  -DmanualInclude=org.example:problematic-library \
  -DreResolve=false

./mvnw verify

The plugin's manualInclude option purges only the coordinates listed. With reResolve=false, the purge and the next build are separate, so the next resolution attempt is visible in the build log.

For dependencies in the current project's resolved tree, use include:

./mvnw dependency:purge-local-repository \
  -Dinclude=org.example:problematic-library \
  -DresolutionFuzziness=version

resolutionFuzziness=version removes files associated with the selected artifact version. Wider values such as artifactId or groupId remove more and should be reserved for cases where that broader scope is intentional.

A manual deletion can also be precise:

rm -rf ~/.m2/repository/org/example/problematic-library/1.2.3
./mvnw verify

On Windows PowerShell:

Remove-Item -Recurse -Force `
  "$HOME\.m2\repository\org\example\problematic-library\1.2.3"
.\mvnw.cmd verify

Do not delete the entire repository merely because one coordinate is suspect.

Read the failure before retrying

Use Maven's diagnostic flags only as long as needed:

./mvnw -e verify
./mvnw -X verify > target/maven-debug.log 2>&1

-e prints execution error details. -X produces extensive debug output, including repository and environment information. Review debug logs for credentials, tokens, usernames, internal hosts, and filesystem paths before sharing them.

The error category determines the next step:

SymptomLikely layerTargeted action
Artifact “not found” shortly after publicationCached resolution or metadataRetry once with -U; verify repository and mirror
401 or 403Credentials or repository permissionsCheck matching <server><id> in settings
Checksum mismatchRepository content or transportStop; compare repository checksum and artifact source
Wrong transitive versionDependency mediationInspect dependency:tree; add dependency management or exclusions
Local passes, CI failsRuntime, settings, or local-only artifactCompare wrapper, Java, effective settings/POM, and dependency tree
Plugin prefix cannot be resolvedPlugin repository metadataVerify plugin repositories and settings; refresh metadata
One artifact is unreadableCorrupt local entryPurge that coordinate only and resolve again

A reproducible CI pattern

An empty local repository can prove that the build does not depend on undeclared local state. It does not need to replace the normal cache on every run.

tmp_repo="$(mktemp -d)"
./mvnw -B -ntp \
  -Dmaven.repo.local="$tmp_repo" \
  clean verify

Use this as a diagnostic or scheduled reproducibility check. Normal CI can cache the local repository using a key that includes relevant POM files, wrapper configuration, Java version, and settings policy. Avoid caching target/ as if it were the dependency repository.

A strong verification sequence is:

  1. run with the project's wrapper;
  2. capture versions and effective configuration;
  3. inspect the dependency tree;
  4. retry with -U only when repository state may have changed;
  5. purge one coordinate if evidence points to a corrupt local artifact;
  6. run clean verify with a fresh temporary repository;
  7. compare the result with CI.

Official sources

The goal is not to avoid caches. The goal is to make cache state observable and disposable at the smallest useful scope. -U, a targeted purge, an effective-configuration diff, and a fresh-repository verification each answer a different question. Used in that order, they fix dependency failures without turning every build into a full redownload.