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