{"record":{"id":"816bb5cd54b0db7b","repo":"jackwener/OpenCLI","slug":"homebrew-api-returned-404-for-url","errorCode":null,"errorMessage":"Homebrew API returned 404 for ${url}.","messagePattern":"Homebrew API returned 404 for (.+?)\\.","errorType":"http","errorClass":"EmptyResultError","httpStatus":404,"severity":"warning","filePath":"clis/homebrew/utils.js","lineNumber":72,"sourceCode":"            `Allowed: ${allowed.join(', ')}.`,\n        );\n    }\n    return s;\n}\n\nexport async function brewFetch(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 formulae.brew.sh is reachable from this network.',\n        );\n    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `Homebrew API returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'Homebrew throttles 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":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/homebrew/utils.js#L54-L90","documentation":"brewFetch treats HTTP 404 from the Homebrew API specially: instead of a generic command error it throws EmptyResultError labeled with the request target, because a 404 from formulae.brew.sh means the formula/cask token (or analytics endpoint) does not exist rather than that something broke. The message includes the full URL for debugging.","triggerScenarios":"Requesting a formula/cask that doesn't exist: brewFetch('https://formulae.brew.sh/api/formula/wgett.json', ...) (typo); using a cask token against the formula endpoint or vice versa; requesting an analytics type/window combination that has no static file.","commonSituations":"Typo'd package names; packages that were renamed or deleted from Homebrew; mixing formula and cask namespaces; stale cached lists of package names referencing removed packages.","solutions":["Verify the token on formulae.brew.sh or with `brew info <token>` and fix the typo.","Check namespace: cask tokens (e.g. 'firefox') must hit /api/cask/, formula tokens /api/formula/.","Handle EmptyResultError as 'not found' in the caller and present an empty/neutral result instead of a hard failure.","If a package disappeared, pin to a known existing alternative or update the package list."],"exampleFix":"// before\nconst f = await formula('wgett'); // 404 -> EmptyResultError\n// after\ntry {\n  const f = await formula('wgett');\n} catch (err) {\n  if (err instanceof EmptyResultError) return null; // treat as not found\n  throw err;\n}\n// or fix the token: await formula('wget');","handlingStrategy":"fallback","validationCode":"const TOKEN = /^[A-Za-z0-9][A-Za-z0-9._+@-]*$/;\nfunction tokenLooksValid(token) {\n  return typeof token === 'string' && token.length <= 100 && TOKEN.test(token);\n}\n// syntactic check only — existence still requires the API (404 = not found)","typeGuard":"null","tryCatchPattern":"try {\n  const data = await brewFetch(url, label);\n  return data;\n} catch (err) {\n  if (err instanceof EmptyResultError) return null; // 404: package not found\n  throw err;\n}","preventionTips":["Verify package names with `brew info <token>` or formulae.brew.sh before scripting.","Keep formula and cask namespaces separate; use the matching /api/formula/ or /api/cask/ path.","Refresh cached package lists periodically — packages get renamed/removed.","Treat EmptyResultError as 'no data', not as an infrastructure failure."],"tags":["http-404","not-found","api"],"backgroundTag":"http-404-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}