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.
Solutions
- Use the exact 'git ls-remote https://github.com/owner/repo.git' output to find a real ref name.
- Pin to a full 40-character commit SHA to bypass ref resolution entirely.
- Use 'main'/'master' or the repo's current default branch if you do not need a specific ref.
- 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
- Pin GitHub registry sources to a full 40-char SHA in production configs.
- Verify branch/tag names with 'git ls-remote' before pinning.
- Prefer refs/heads/<name> or refs/tags/<name> over bare names when ambiguous.
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
- FETCH_ERROR
- FETCH_ERROR
- Could not resolve the following aliases in
- Failed to load .json.
- Failed to read GitHub source file
AI-assisted analysis of shadcn-ui/ui@683a5a9b37 (2026-08-12).
Data as JSON: /api/errors/96377209b5dc9778.
Report an issue: GitHub.
Appendix: source
Thrown at packages/shadcn/src/registry/github-ref.ts:99
ref,
repoUrl,
error
)
if (!options.authAnchor) {
throw refError
}
return resolveGitHubRefWithAuth(address, ref, options.authAnchor, refError)
}
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 683a5a9b37)