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
- Verify the exact crate name on crates.io (or run the search subcommand) and correct the spelling/normalization (e.g. serde_json vs serdejson).
- Check the crate/version actually exists: cargo search <name> or visit https://crates.io/crates/<name> in a browser.
- If the name is confirmed, re-run later in case of a transient backend issue; otherwise treat it as a genuine empty result.
- 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
- Resolve crate names via the search subcommand instead of typing them by hand.
- Remember crates.io name normalization (underscores vs hyphens).
- Treat EmptyResultError as a normal 'not found' outcome in scripts.
- Verify rare/old crates exist before scripting bulk lookups.
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
- Chess.com returned 404 for ${url}
- crates crate
- Maven Central returned 404 for ${url}.
- zhihu answer-detail
- bilibili creator-stats ${bvid}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/9474d80edd32e900.
Report an issue: GitHub.