Description
In May 2026 Sonatype significantly tightened the rate limits for Maven Central (see the FAQ). As a result, 429 Too Many Requests responses are now routinely returned to clients that fetch many artifacts from a single IP — including Trivy when the local ~/.m2 cache is empty or partially populated.
When the Maven pom parser receives 429 Too Many Requests from a remote repository (Maven Central or any other configured remote), the response is logged at DEBUG and the dependency is skipped without surfacing any warning or error to the user. Trivy completes the scan and reports "no issues" even when a large portion of the dependency tree was never resolved.
Reported by users in #10672.
What did you expect to happen?
On 429 from a remote Maven repository, Trivy aborts the scan with a clear error stating which URL was rate-limited, the Retry-After value if provided, and how to mitigate (populate the local Maven cache before scanning).
What happened instead?
The scan exits successfully with WARN [report] Supported files for scanner(s) not found. or with a report that omits Maven dependencies. The 429 is only visible at --debug log level.
Why retry / continue does not work
I measured the actual behaviour of Maven Central by running controlled HTTP probes from a single IP, both as a parallel burst and as a steady sequential stream that mimics how the pom parser walks the dependency queue.
Threshold
| Pattern |
Result |
| Sequential, ~4 RPS, distinct artifacts |
First 429 at request #100 (~24 sec from start) |
| Parallel, 50 workers, 389 URLs |
First 429 after ~40 successful edge-cache hits, ~86% of remaining requests 429 |
Block duration on the first hit
The block is communicated via Retry-After. Observed values on the very first violation from a clean state:
| Trigger |
Retry-After |
| Parallel burst |
1586 sec (~26 min) |
| Sequential ~4 RPS |
1800 sec (30 min) |
Per the Sonatype FAQ, repeated requests during the block extend it up to 24 hours.
The block applies to everything, including cache
During the block, every request from the offending IP returns 429 — including requests for artifacts that would normally be served from the Cloudflare edge cache and would not have hit Maven Central's origin at all. Verified:
HTTP/1.1 429 Too Many Requests
Retry-After: 1565
…against commons-lang3-3.12.0.pom which is permanently cached at the edge (CF-Cache-Status: HIT outside the block).
Retry budget is pointless
A "retry-with-Retry-After" strategy fails because:
- The minimum observed wait is ~26 min for a first violation, and grows on repeat. A scan of a non-trivial project will hit this on the first artifact and stall the pipeline for the entire
Retry-After duration.
- During the block, even cached HITs return
429. The remaining ~N unresolved artifacts cannot be fetched until the block clears, so retrying per-artifact serially gives no improvement.
- Continuing the scan and skipping the failed artifact (current behaviour) is equivalent to silently producing an incomplete result, because all subsequent fetches from the same IP will also fail.
The only behaviour that does not produce silently incomplete data is to abort the scan on the first 429 with a clear, actionable error.
Reproduction
-
Trigger the rate limit from the host that will run Trivy (e.g. burst a few hundred parallel requests to varied POMs that bypass the edge cache).
-
Run Trivy against any Java project whose POM resolves dependencies not present in ~/.m2:
trivy fs --debug ./project
Expected: scan aborts with explanatory error.
Actual: scan finishes successfully; only a DEBUG [pom] Failed to fetch ... statusCode=429 line in the log.
Root Cause
Two layers conspire to hide the failure:
- The
pom parser uniformly downgrades any non-200 response to a debug log + soft skip. 404 (artifact not found) and 429 (we are rate-limited) are treated identically, even though their semantics are completely different.
- The analyzer layer swallows every parser error except
context.DeadlineExceeded, with no escape hatch for "this is fatal, surface it".
A 429 must be distinguished from 404: the former implies the scan is guaranteed to be incomplete because the rate limit applies to every subsequent request, not only to the offending artifact.
Environment
- Trivy: any current version (behaviour unchanged since the POM parser was introduced)
- Target: any Java project that triggers transitive resolution against a remote Maven repository
Description
In May 2026 Sonatype significantly tightened the rate limits for Maven Central (see the FAQ). As a result,
429 Too Many Requestsresponses are now routinely returned to clients that fetch many artifacts from a single IP — including Trivy when the local~/.m2cache is empty or partially populated.When the Maven
pomparser receives429 Too Many Requestsfrom a remote repository (Maven Central or any other configured remote), the response is logged atDEBUGand the dependency is skipped without surfacing any warning or error to the user. Trivy completes the scan and reports "no issues" even when a large portion of the dependency tree was never resolved.Reported by users in #10672.
What did you expect to happen?
On
429from a remote Maven repository, Trivy aborts the scan with a clear error stating which URL was rate-limited, theRetry-Aftervalue if provided, and how to mitigate (populate the local Maven cache before scanning).What happened instead?
The scan exits successfully with
WARN [report] Supported files for scanner(s) not found.or with a report that omits Maven dependencies. The 429 is only visible at--debuglog level.Why retry / continue does not work
I measured the actual behaviour of Maven Central by running controlled HTTP probes from a single IP, both as a parallel burst and as a steady sequential stream that mimics how the
pomparser walks the dependency queue.Threshold
429at request #100 (~24 sec from start)429after ~40 successful edge-cache hits, ~86% of remaining requests 429Block duration on the first hit
The block is communicated via
Retry-After. Observed values on the very first violation from a clean state:Retry-AfterPer the Sonatype FAQ, repeated requests during the block extend it up to 24 hours.
The block applies to everything, including cache
During the block, every request from the offending IP returns
429— including requests for artifacts that would normally be served from the Cloudflare edge cache and would not have hit Maven Central's origin at all. Verified:…against
commons-lang3-3.12.0.pomwhich is permanently cached at the edge (CF-Cache-Status: HIToutside the block).Retry budget is pointless
A "retry-with-
Retry-After" strategy fails because:Retry-Afterduration.429. The remaining ~N unresolved artifacts cannot be fetched until the block clears, so retrying per-artifact serially gives no improvement.The only behaviour that does not produce silently incomplete data is to abort the scan on the first
429with a clear, actionable error.Reproduction
Trigger the rate limit from the host that will run Trivy (e.g. burst a few hundred parallel requests to varied POMs that bypass the edge cache).
Run Trivy against any Java project whose POM resolves dependencies not present in
~/.m2:Expected: scan aborts with explanatory error.
Actual: scan finishes successfully; only a
DEBUG [pom] Failed to fetch ... statusCode=429line in the log.Root Cause
Two layers conspire to hide the failure:
pomparser uniformly downgrades any non-200 response to a debug log + soft skip.404(artifact not found) and429(we are rate-limited) are treated identically, even though their semantics are completely different.context.DeadlineExceeded, with no escape hatch for "this is fatal, surface it".A
429must be distinguished from404: the former implies the scan is guaranteed to be incomplete because the rate limit applies to every subsequent request, not only to the offending artifact.Environment