{"record":{"id":"17e6dbb9f4be9e7c","repo":"jackwener/OpenCLI","slug":"npm-registry-returned-404-for-url","errorCode":null,"errorMessage":"npm registry returned 404 for ${url}.","messagePattern":"npm registry returned 404 for (.+?)\\.","errorType":"exception","errorClass":"EmptyResultError","httpStatus":404,"severity":"warning","filePath":"clis/npm/utils.js","lineNumber":57,"sourceCode":"    if (n > maxValue) {\n        throw new ArgumentError(`npm ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\nexport async function npmFetch(url, label) {\n    let resp;\n    try {\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 registry.npmjs.org / api.npmjs.org are reachable from this network.',\n        );\n    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `npm registry returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'npm throttles unauthenticated bursts; 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":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/npm/utils.js#L39-L75","documentation":"npmFetch wraps the public npm registry HTTP API (registry.npmjs.org / api.npmjs.org) and converts non-success responses into typed errors. When the endpoint answers HTTP 404, the URL could not be resolved to any known resource, so npmFetch throws EmptyResultError to signal 'nothing found' rather than a hard failure. Callers (like the body wrapper) use this to distinguish missing packages from genuine network or server errors.","triggerScenarios":"Any npmFetch call whose URL resolves to HTTP 404: fetching metadata for a package name that does not exist on the registry, a misspelled or renamed package, a scoped package without correct URL-encoding (e.g. '@scope/name' not encoded as @scope%2fname), or a deleted/unpublished package.","commonSituations":"Typos in a package name passed by a user ('reactt' instead of 'react'); querying a private-scope package on the public registry where it was never published; packages removed by npm for policy violations; stale scripts referencing packages that were renamed or deprecated.","solutions":["Verify the package name spelling with `npm view <name>` or on npmjs.com before retrying","Check the exact URL being fetched; for scoped packages ensure the name is URL-encoded correctly (e.g. @babel%2fcore)","If the package was unpublished or renamed, update to the correct/current name","Handle EmptyResultError distinctly in calling code so 'not found' is reported as an empty result, not a crash"],"exampleFix":"// before\nconst data = await npmFetch(`${NPM_REGISTRY}/${encodeURIComponent(name)}`, 'npm package');\n// after\ntry {\n  const data = await npmFetch(`${NPM_REGISTRY}/${encodeURIComponent(name)}`, 'npm package');\n} catch (err) {\n  if (err instanceof EmptyResultError) {\n    console.error(`Package \"${name}\" was not found on the npm registry.`);\n    return null;\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"function isValidNpmName(name) {\n  const s = String(name ?? '').trim();\n  return s.length > 0 && s.length <= 214 &&\n    /^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$/i.test(s);\n}\nif (!isValidNpmName(pkg)) throw new Error(`Invalid npm package name: ${pkg}`);","typeGuard":"function isEmptyResultError(err) {\n  return err instanceof Error && err.name === 'EmptyResultError';\n}","tryCatchPattern":"try {\n  const data = await npmFetch(`${NPM_REGISTRY}/${encodeURIComponent(pkg)}`, 'npm package');\n  return data;\n} catch (err) {\n  if (err instanceof EmptyResultError || isEmptyResultError(err)) {\n    return null; // package not found — treat as empty result\n  }\n  throw err;\n}","preventionTips":["Validate package names against the npm naming rules before calling the API","URL-encode scoped package names (e.g. encodeURIComponent handles '@scope/name' → '@scope%2Fname')","Check the package exists on npmjs.com when a query result is surprising","Treat EmptyResultError as 'no data' rather than a crash in pipelines processing many packages"],"tags":["npm","http-404","registry","not-found"],"backgroundTag":"http-404-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}