shadcn-ui/ui · error · RegistrySourceFileError

FETCH_ERROR

FETCH_ERROR

Error message

Could not resolve GitHub ref "${ref}" for ${address.owner}/${address.repo}.

What it means

Thrown by resolveGitHubRefUncached after 'git ls-remote' succeeded but none of the preferred ref names (refs/heads/<ref>, refs/tags/<ref>, etc.) matched any advertised ref. RegistrySourceFileError signals that the ref string itself is syntactically fine but does not exist in the repository at the resolved default or specified ref.

Source

Thrown at packages/shadcn/src/registry/github-ref.ts:72

          GIT_TERMINAL_PROMPT: "0",
        },
        timeout: GITHUB_REF_RESOLUTION_TIMEOUT,
      }
    )
    stdout = result.stdout
  } catch (error) {
    throw createGitHubRefResolutionError(address, ref, repoUrl, error)
  }

  const refs = parseGitLsRemote(stdout)
  for (const candidate of getPreferredGitHubRefNames(ref)) {
    const sha = refs.get(candidate)
    if (sha) {
      return sha
    }
  }

  throw new RegistrySourceFileError("registry.json", undefined, {
    message: `Could not resolve GitHub ref "${ref}" for ${address.owner}/${address.repo}.`,
    context: {
      reason: "github-ref-resolution",
      source: formatGitHubSource(address),
      ref,
      repoUrl,
    },
    suggestion:
      'Use an existing branch, tag, or full commit SHA. For example: "owner/repo/item#main" or "owner/repo/item#v1.0.0".',
  })
}

export function getGitHubRefCandidates(ref: string) {
  return Array.from(new Set(getPreferredGitHubRefNames(ref)))
}

export function getPreferredGitHubRefNames(ref: string) {
  if (ref === "HEAD") {

View on GitHub (pinned to efac598707)

Solutions

  1. Use the exact 'git ls-remote https://github.com/owner/repo.git' output to find a real ref name.
  2. Pin to a full 40-character commit SHA to bypass ref resolution entirely.
  3. Use 'main'/'master' or the repo's current default branch if you do not need a specific ref.
  4. Check whether the ref lives under refs/heads vs refs/tags and qualify it ('refs/tags/v1.0.0').

Example fix

// before
add("owner/repo/button#v0.9")

// after (verify with git ls-remote)
add("owner/repo/button#v1.0.0")
// or a commit SHA
add("owner/repo/button#a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2")
Defensive patterns

Strategy: validation

Validate before calling

import { execa } from "execa";

async function refExists(owner: string, repo: string, ref: string) {
  const candidates = [
    ref === "HEAD" ? "HEAD" : null,
    ref.startsWith("refs/tags/") ? `${ref}^{}` : null,
    ref.startsWith("refs/") ? ref : null,
    `refs/heads/${ref}`, `refs/tags/${ref}^{}`, `refs/tags/${ref}`, ref,
  ].filter(Boolean) as string[];
  const { stdout } = await execa("git", ["ls-remote", "--symref", "--", `https://github.com/${owner}/${repo}.git`, ...candidates]);
  return /\b[0-9a-f]{40}\b/.test(stdout);
}
// call before resolveGitHubRef to fail with a clear, local error

Type guard

const SHA_40 = /^[a-fA-F0-9]{40}$/;
function isFullCommitSha(ref: string): boolean {
  return SHA_40.test(ref); // SHAs short-circuit ref resolution and never hit this error
}

Try / catch

try {
  await resolveGitHubRef(address);
} catch (err) {
  if (err instanceof RegistrySourceFileError && /Could not resolve GitHub ref/.test(err.message)) {
    // fall back to the default branch or a known-good SHA
  }
  throw err;
}

Prevention

When it happens

Trigger: Pinning to a branch or tag that was deleted/renamed, a typo in the ref, or asking for a tag like 'v1.0.0' that does not exist. The 40-char SHA fast-path and the ls-remote call both ran without error; only the lookup failed.

Common situations: Branch renamed or deleted after an address was pinned, semantic confusion between branch and tag names, or referencing a ref from a fork that does not exist in the resolved repo.

Related errors


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