jackwener/OpenCLI · error · CommandExecutionError

${label} returned HTTP 429 (rate limited)

Error message

${label} returned HTTP 429 (rate limited)

What it means

mavenFetch() detects HTTP 429 from search.maven.org and throws a CommandExecutionError indicating the request was rate limited. Maven Central's Solr endpoint throttles bursts of requests.

Source

Thrown at clis/maven/utils.js:86

    return { groupId, artifactId, version: version ?? null };
}

export async function mavenFetch(url, label) {
    let resp;
    try {
        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });
    }
    catch (err) {
        throw new CommandExecutionError(
            `${label} request failed: ${err?.message ?? err}`,
            'Check that search.maven.org is reachable from this network.',
        );
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `Maven Central returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'Maven Central throttles bursts; wait a few seconds and retry.',
        );
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
    }
    let body;
    try {
        body = await resp.json();
    }
    catch (err) {
        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
    }
    return body;
}

/** Convert epoch-ms (Maven Solr `timestamp`) to ISO-8601 UTC. Returns null for falsy/invalid. */

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait a few seconds and retry — the throttle is temporary.
  2. Add exponential backoff with jitter around mavenFetch calls.
  3. Rate-limit your own requests (e.g. a few hundred ms between lookups).
  4. Batch queries where possible (Solr rows/q parameter) instead of one request per coordinate.

Example fix

// before
for (const c of coords) results.push(await lookup(c));
// after
for (const c of coords) {
  results.push(await withBackoff(() => lookup(c)));
  await sleep(250); // stay under Maven Central rate limits
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

async function withBackoff(fn, attempts = 4) {
  for (let i = 0; ; i++) {
    try { return await fn(); }
    catch (err) {
      if (!/429/.test(err.message) || i >= attempts) throw err;
      await sleep(2 ** i * 500 + Math.random() * 250);
    }
  }
}
const data = await withBackoff(() => mavenFetch(url, 'search'));

Prevention

When it happens

Trigger: Making many maven lookups in rapid succession (batch scripts, CI loops, fan-out queries) until search.maven.org responds 429.

Common situations: Iterating over hundreds of dependencies with no delay; shared CI runners where many jobs hit the same unauthenticated endpoint; retry loops without backoff amplifying the throttling.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/c96b394091f8be96. Report an issue: GitHub.