shadcn-ui/ui · error · RegistryFetchError

FETCH_ERROR

FETCH_ERROR

Error message

Failed to fetch from registry (${statusCode}): ${url}

What it means

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.

Source

Thrown at packages/shadcn/src/registry/fetcher.ts:106

            }

            if (response.status === 401) {
              throw new RegistryUnauthorizedError(url, messageFromServer)
            }

            if (response.status === 404) {
              throw new RegistryNotFoundError(url, messageFromServer)
            }

            if (response.status === 410) {
              throw new RegistryGoneError(url, messageFromServer)
            }

            if (response.status === 403) {
              throw new RegistryForbiddenError(url, messageFromServer)
            }

            throw new RegistryFetchError(
              url,
              response.status,
              messageFromServer
            )
          }

          return response.json()
        })()

        if (options.useCache) {
          registryCache.set(cacheKey, fetchPromise)
        }
        return fetchPromise
      })
    )

    return results
  } catch (error) {

View on GitHub (pinned to efac598707)

Solutions

  1. Retry with backoff for 5xx and 429 responses.
  2. For 429, reduce request frequency or add an auth token that raises the rate limit.
  3. For 4xx other than the auth/not-found family, inspect the server message (attached as cause) and fix the request.
  4. Check the registry's status page if available.
Defensive patterns

Strategy: retry

Validate before calling

function isRetryableStatus(status?: number): boolean {
  return status === 429 || (typeof status === "number" && status >= 500);
}
// decide whether to retry based on err.statusCode before looping

Type guard

function isTransientFetchError(err: unknown): boolean {
  return err instanceof RegistryFetchError && isRetryableStatus(err.statusCode);
}

Try / catch

async function fetchWithRetry(url: string[], opts: { useCache?: boolean }) {
  for (const wait of [0, 1000, 4000]) {
    try {
      return await fetchRegistry(url, opts);
    } catch (err) {
      if (err instanceof RegistryFetchError && isRetryableStatus(err.statusCode) && wait < 4000) {
        await new Promise(r => setTimeout(r, wait));
        continue;
      }
      throw err;
    }
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12). Data as JSON: /api/errors/5c5aed65b0b0f093. Report an issue: GitHub.