jackwener/OpenCLI · warning · EmptyResultError

crates.io returned 404 for ${url}.

Error message

crates.io returned 404 for ${url}.

What it means

cratesFetch in clis/crates/utils.js wraps every crates.io HTTP request. When the response status is 404 it throws EmptyResultError, signaling that the requested crates.io resource (crate, version, dependency set, etc.) does not exist rather than a transport failure. The library uses the 404-specific error type so callers can distinguish 'no such resource' from network/rate-limit problems.

Source

Thrown at clis/crates/utils.js:53

        throw new ArgumentError(`crates ${label} must be <= ${maxValue}`);
    }
    return n;
}

export async function cratesFetch(url, label) {
    let resp;
    try {
        // crates.io requires a descriptive User-Agent per https://crates.io/data-access
        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });
    }
    catch (err) {
        throw new CommandExecutionError(
            `${label} request failed: ${err?.message ?? err}`,
            'Check that crates.io is reachable from this network.',
        );
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `crates.io returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'crates.io rate-limits unauthenticated traffic; 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;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the exact crate name on crates.io (or run the search subcommand) and correct the spelling/normalization (e.g. serde_json vs serdejson).
  2. Check the crate/version actually exists: cargo search <name> or visit https://crates.io/crates/<name> in a browser.
  3. If the name is confirmed, re-run later in case of a transient backend issue; otherwise treat it as a genuine empty result.
  4. Catch EmptyResultError in your script and handle 'not found' as a normal, non-exceptional outcome.

Example fix

// before
const crate = await opencli.crates.info('tokyo'); // typo -> 404
// after
const results = await opencli.crates.search('tokyo');
const crate = results[0] ? await opencli.crates.info(results[0].name) : null;
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the crate name shape before calling (adapter also enforces this)
const CRATE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
if (!CRATE_NAME.test(name)) throw new Error(`invalid crate name: ${name}`);

Type guard

function isNotFoundError(err) { return err && err.name === 'EmptyResultError'; }

Try / catch

try {
  const info = await opencli.crates.info(name);
} catch (err) {
  if (err.name === 'EmptyResultError') {
    // treat as 'crate not found', not a failure
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: Any crates subcommand (search, info, deps, etc.) that calls cratesFetch with a URL crates.io answers 404 to: fetching a crate by a misspelled or deleted name (e.g. `opencli crates info serd`), or a nonexistent version/dependency path.

Common situations: Typo in the crate name; using a hyphen vs underscore incorrectly (crates.io normalizes some but the URL must match); the crate was yanked/removed/renamed; querying a version that was never published.

Related errors


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