shadcn-ui/ui · error · Error
Invalid target path "${target}". Target paths using @${alias
Error message
Invalid target path "${target}". Target paths using @${aliasKey}/ must stay within the ${aliasKey} alias root. What it means
Thrown by resolveAliasTarget when a file target of the form @aliasKey/subpath resolves to a path outside the alias root. After matching ^@([^/]+)/(.+)$ and confirming aliasKey is a known target alias, it computes aliasRoot = config.resolvedPaths[aliasKey] and resolvedPath = resolve(aliasRoot, targetPath); if resolvedPath is neither the aliasRoot itself nor beneath it, the target escapes and is rejected.
Source
Thrown at packages/shadcn/src/utils/updaters/update-files.ts:475
return null
}
const [, aliasKey, targetPath] = match
if (!isTargetAliasKey(aliasKey)) {
return {
target: `${aliasKey}/${targetPath}`,
}
}
const aliasRoot = path.resolve(config.resolvedPaths[aliasKey])
const resolvedPath = path.resolve(aliasRoot, targetPath)
if (
resolvedPath !== aliasRoot &&
!resolvedPath.startsWith(`${aliasRoot}${path.sep}`)
) {
throw new Error(
`Invalid target path "${target}". Target paths using @${aliasKey}/ must stay within the ${aliasKey} alias root.`
)
}
return {
resolvedPath,
}
}
function resolveFileTargetDirectory(
file: z.infer<typeof registryItemFileSchema>,
config: Config
) {
if (file.type === "registry:ui") {
return config.resolvedPaths.ui
}
if (file.type === "registry:lib") {View on GitHub (pinned to efac598707)
Solutions
- Edit the offending target to remove ".." segments so it stays inside the alias root.
- Use a target that the alias root legitimately contains, e.g. "@components/ui/button.tsx".
- If you need a file in a different alias, switch the target to that alias's prefix.
- Re-run the add command after the registry item is fixed.
Example fix
// registry item (before)
{ "path": "x.ts", "target": "@components/../lib/x.ts", "type": "registry:ui" }
// after
{ "path": "x.ts", "target": "@lib/x.ts", "type": "registry:ui" } Defensive patterns
Strategy: validation
Validate before calling
import path from "path"
function assertAliasTargetInside(target: string, aliasRoot: string) {
const resolved = path.resolve(aliasRoot, target.replace(/^@[^/]+\//, ""))
if (resolved !== aliasRoot && !resolved.startsWith(aliasRoot + path.sep)) {
throw new Error(`target escapes alias root: ${target}`)
}
} Type guard
const isAliasTargetInside = (target: string, aliasRoot: string) => {
const m = target.match(/^@([^/]+)\/(.+)$/)
if (!m) return true
const resolved = path.resolve(aliasRoot, m[2])
return resolved === aliasRoot || resolved.startsWith(aliasRoot + path.sep)
} Try / catch
try {
await updateFiles(tree, config, options)
} catch (e) {
if (e instanceof Error && /must stay within the .* alias root/.test(e.message)) {
// rewrite the target to remove ".." and retry
}
throw e
} Prevention
- Keep @alias/subpath targets flat beneath the alias root.
- Never put ".." in a target after the alias prefix.
- Switch the alias prefix rather than traversing out of one.
- Lint registry targets in CI for traversal sequences.
When it happens
Trigger: A registry file uses target like "@components/../lib/secret" so that path.resolve pushes it above the components alias root. The containment check `resolvedPath !== aliasRoot && !resolvedPath.startsWith(aliasRoot + sep)` fails and throws.
Common situations: Hand-written target with "../" that climbs out of the alias directory; refactoring an alias root without updating targets; malicious registry trying to write outside the alias.
Related errors
- Cannot write to ${filePath}: path exists and is a directory.
- A full project config is required to resolve target aliases.
- We found an unsafe file path "${locationField}" in the regis
- Could not back up ${filePath}.
- Could not resolve the following aliases in ${highlighter.inf
AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12).
Data as JSON: /api/errors/6d85eac41b27627a.
Report an issue: GitHub.