apache/maven · error · RepositoryMetadataReadException

Cannot read metadata from

Error message

Cannot read metadata from '{}': {}

What it means

DefaultRepositoryMetadataManager.readMetadata() opens a maven-metadata.xml file with Files.newInputStream and parses it with a StAX reader; any IOException (permissions, truncated file, disk error) or XMLStreamException (malformed XML) is wrapped in RepositoryMetadataReadException with this message. The placeholders are the metadata file path and the underlying parser/IO message. A missing file is tolerated by callers, so this specifically means the file exists but cannot be read or parsed.

Solutions

  1. Delete the metadata file named in the message and rebuild with -U so Maven re-downloads it: rm ~/.m2/repository/<group>/<artifact>/maven-metadata*.xml
  2. Inspect the first lines of that file (head) for an HTML error page or truncated XML; if it came from a remote repository, purge that metadata on the repository manager too
  3. If the message is a permission or IO error, fix ownership/permissions on the local repository (chown -R) and check disk space
  4. If a CI cache ships the local repository, invalidate the cache after repairing so the corrupt file does not come back

Example fix

# before: corrupt file left in place, build fails on every run
head -3 ~/.m2/repository/com/example/art/maven-metadata.xml   # shows HTML or truncated XML
mvn clean install
# after: remove the corrupt metadata and force a refresh
rm ~/.m2/repository/com/example/art/maven-metadata*.xml
mvn -U clean install
Defensive patterns

Strategy: try-catch

Validate before calling

File f = new File(localRepository.getBasedir(),
        localRepository.pathOfLocalRepositoryMetadata(metadata, remoteRepository));
if (f.isFile()) {
    try {
        javax.xml.parsers.DocumentBuilderFactory.newInstance()
                .newDocumentBuilder().parse(f); // fails fast on corrupt XML
    } catch (Exception e) {
        // delete/repair the metadata file before resolving
    }
}

Try / catch

Catch RepositoryMetadataReadException around resolve()/resolveAlways()/deploy() call sites; treat it as a local-cache defect: delete the file named in the message and retry once, rethrow if it persists.

Prevention

When it happens

Trigger: Any metadata-consuming path that calls readMetadata(): resolveAlways(), deploy()'s retrieval of previous metadata, or snapshot/version metadata merging, when the local maven-metadata.xml is hand-edited into invalid XML, truncated by a killed build, zero bytes, an HTML error page cached by a mirror, or unreadable due to permissions or file locking.

Common situations: A build killed (Ctrl-C, CI timeout) while maven-metadata.xml is being written; a repository proxy returning an HTML 500 page that gets saved as metadata; antivirus or a read-only ~/.m2/repository; CI jobs caching a corrupt local repository between runs.

Related errors


AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21). Data as JSON: /api/errors/f9d5561770793456. Report an issue: GitHub.

Appendix: source

Thrown at compat/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/DefaultRepositoryMetadataManager.java:280

            } else {
                repoMetadata.setMetadata(metadata);
                setRepository = true;
            }
        }
        return setRepository;
    }

    /*
     * TODO share with DefaultPluginMappingManager.
     */
    protected Metadata readMetadata(File mappingFile) throws RepositoryMetadataReadException {

        try (InputStream in = Files.newInputStream(mappingFile.toPath())) {
            return new Metadata(new MetadataStaxReader().read(in, false));
        } catch (FileNotFoundException e) {
            throw new RepositoryMetadataReadException("Cannot read metadata from '" + mappingFile + "'", e);
        } catch (IOException | XMLStreamException e) {
            throw new RepositoryMetadataReadException(
                    "Cannot read metadata from '" + mappingFile + "': " + e.getMessage(), e);
        }
    }

    /**
     * Ensures the last updated timestamp of the specified metadata does not refer to the future and fixes the local
     * metadata if necessary to allow proper merging/updating of metadata during deployment.
     */
    private void fixTimestamp(File metadataFile, Metadata metadata, Metadata reference) {
        boolean changed = false;

        if (metadata != null && reference != null) {
            Versioning versioning = metadata.getVersioning();
            Versioning versioningRef = reference.getVersioning();
            if (versioning != null && versioningRef != null) {
                String lastUpdated = versioning.getLastUpdated();
                String now = versioningRef.getLastUpdated();
                if (lastUpdated != null && now != null && now.compareTo(lastUpdated) < 0) {

View on GitHub (pinned to e4093d4e12)