{"record":{"id":"9474d80edd32e900","repo":"jackwener/OpenCLI","slug":"crates-io-returned-404-for-url","errorCode":null,"errorMessage":"crates.io returned 404 for ${url}.","messagePattern":"crates\\.io returned 404 for (.+?)\\.","errorType":"http","errorClass":"EmptyResultError","httpStatus":404,"severity":"warning","filePath":"clis/crates/utils.js","lineNumber":53,"sourceCode":"        throw new ArgumentError(`crates ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\nexport async function cratesFetch(url, label) {\n    let resp;\n    try {\n        // crates.io requires a descriptive User-Agent per https://crates.io/data-access\n        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });\n    }\n    catch (err) {\n        throw new CommandExecutionError(\n            `${label} request failed: ${err?.message ?? err}`,\n            'Check that crates.io is reachable from this network.',\n        );\n    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `crates.io returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'crates.io rate-limits unauthenticated traffic; wait a few seconds and retry.',\n        );\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);\n    }\n    let body;\n    try {\n        body = await resp.json();\n    }\n    catch (err) {\n        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);\n    }\n    return body;","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/crates/utils.js#L35-L71","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst crate = await opencli.crates.info('tokyo'); // typo -> 404\n// after\nconst results = await opencli.crates.search('tokyo');\nconst crate = results[0] ? await opencli.crates.info(results[0].name) : null;","handlingStrategy":"try-catch","validationCode":"// validate the crate name shape before calling (adapter also enforces this)\nconst CRATE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;\nif (!CRATE_NAME.test(name)) throw new Error(`invalid crate name: ${name}`);","typeGuard":"function isNotFoundError(err) { return err && err.name === 'EmptyResultError'; }","tryCatchPattern":"try {\n  const info = await opencli.crates.info(name);\n} catch (err) {\n  if (err.name === 'EmptyResultError') {\n    // treat as 'crate not found', not a failure\n    return null;\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["http-404","crates-io","not-found","empty-result"],"backgroundTag":"http-404-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}