{"record":{"id":"5c5aed65b0b0f093","repo":"shadcn-ui/ui","slug":"fetch-error","errorCode":"FETCH_ERROR","errorMessage":"Failed to fetch from registry (${statusCode}): ${url}","messagePattern":"Failed to fetch from registry \\((.+?)\\): (.+?)","errorType":"http","errorClass":"RegistryFetchError","httpStatus":null,"severity":"error","filePath":"packages/shadcn/src/registry/fetcher.ts","lineNumber":106,"sourceCode":"            }\n\n            if (response.status === 401) {\n              throw new RegistryUnauthorizedError(url, messageFromServer)\n            }\n\n            if (response.status === 404) {\n              throw new RegistryNotFoundError(url, messageFromServer)\n            }\n\n            if (response.status === 410) {\n              throw new RegistryGoneError(url, messageFromServer)\n            }\n\n            if (response.status === 403) {\n              throw new RegistryForbiddenError(url, messageFromServer)\n            }\n\n            throw new RegistryFetchError(\n              url,\n              response.status,\n              messageFromServer\n            )\n          }\n\n          return response.json()\n        })()\n\n        if (options.useCache) {\n          registryCache.set(cacheKey, fetchPromise)\n        }\n        return fetchPromise\n      })\n    )\n\n    return results\n  } catch (error) {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/shadcn-ui/ui/blob/efac5987074af84ece57c367c6dd83387b967022/packages/shadcn/src/registry/fetcher.ts#L88-L124","documentation":"Thrown by fetchRegistry as the catch-all for any non-ok response status not covered by 401, 403, 404, or 410. RegistryFetchError records the status code, URL, and any server-provided message. Typical statuses that land here: 5xx server errors, 429 rate limiting, 400 bad request, 308/redirects not followed.","triggerScenarios":"Registry server returns 500/502/503 (down or deploying), 429 (rate limited), 400 (malformed request from a bad URL template), or an unexpected 3xx. Each surfaces here with its status code embedded in the message.","commonSituations":"Transient server outage, hitting a public registry's rate limit from CI, a misconfigured URL template producing an invalid request, or a CDN returning a 5xx during a deploy.","solutions":["Retry with backoff for 5xx and 429 responses.","For 429, reduce request frequency or add an auth token that raises the rate limit.","For 4xx other than the auth/not-found family, inspect the server message (attached as cause) and fix the request.","Check the registry's status page if available."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"function isRetryableStatus(status?: number): boolean {\n  return status === 429 || (typeof status === \"number\" && status >= 500);\n}\n// decide whether to retry based on err.statusCode before looping","typeGuard":"function isTransientFetchError(err: unknown): boolean {\n  return err instanceof RegistryFetchError && isRetryableStatus(err.statusCode);\n}","tryCatchPattern":"async function fetchWithRetry(url: string[], opts: { useCache?: boolean }) {\n  for (const wait of [0, 1000, 4000]) {\n    try {\n      return await fetchRegistry(url, opts);\n    } catch (err) {\n      if (err instanceof RegistryFetchError && isRetryableStatus(err.statusCode) && wait < 4000) {\n        await new Promise(r => setTimeout(r, wait));\n        continue;\n      }\n      throw err;\n    }\n  }\n}","preventionTips":["Retry only 429 and 5xx; fail fast on 4xx client errors.","Cache successful registry responses to reduce load and rate-limit risk.","Log err.statusCode and the server message (err.responseBody) to triage 4xx causes."],"tags":["network","fetch-error","registry","retry","typescript"],"backgroundTag":null,"analyzedSha":"efac5987074af84ece57c367c6dd83387b967022","analyzedAt":"2026-08-12T05:00:50.218Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}