{"record":{"id":"6d85eac41b27627a","repo":"shadcn-ui/ui","slug":"invalid-target-path-target-target-paths-usin","errorCode":null,"errorMessage":"Invalid target path \"${target}\". Target paths using @${aliasKey}/ must stay within the ${aliasKey} alias root.","messagePattern":"Invalid target path \"(.+?)\"\\. Target paths using @(.+?)/ must stay within the (.+?) alias root\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shadcn/src/utils/updaters/update-files.ts","lineNumber":475,"sourceCode":"    return null\n  }\n\n  const [, aliasKey, targetPath] = match\n\n  if (!isTargetAliasKey(aliasKey)) {\n    return {\n      target: `${aliasKey}/${targetPath}`,\n    }\n  }\n\n  const aliasRoot = path.resolve(config.resolvedPaths[aliasKey])\n  const resolvedPath = path.resolve(aliasRoot, targetPath)\n\n  if (\n    resolvedPath !== aliasRoot &&\n    !resolvedPath.startsWith(`${aliasRoot}${path.sep}`)\n  ) {\n    throw new Error(\n      `Invalid target path \"${target}\". Target paths using @${aliasKey}/ must stay within the ${aliasKey} alias root.`\n    )\n  }\n\n  return {\n    resolvedPath,\n  }\n}\n\nfunction resolveFileTargetDirectory(\n  file: z.infer<typeof registryItemFileSchema>,\n  config: Config\n) {\n  if (file.type === \"registry:ui\") {\n    return config.resolvedPaths.ui\n  }\n\n  if (file.type === \"registry:lib\") {","sourceCodeStart":457,"sourceCodeEnd":493,"githubUrl":"https://github.com/shadcn-ui/ui/blob/efac5987074af84ece57c367c6dd83387b967022/packages/shadcn/src/utils/updaters/update-files.ts#L457-L493","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// registry item (before)\n{ \"path\": \"x.ts\", \"target\": \"@components/../lib/x.ts\", \"type\": \"registry:ui\" }\n// after\n{ \"path\": \"x.ts\", \"target\": \"@lib/x.ts\", \"type\": \"registry:ui\" }","handlingStrategy":"validation","validationCode":"import path from \"path\"\nfunction assertAliasTargetInside(target: string, aliasRoot: string) {\n  const resolved = path.resolve(aliasRoot, target.replace(/^@[^/]+\\//, \"\"))\n  if (resolved !== aliasRoot && !resolved.startsWith(aliasRoot + path.sep)) {\n    throw new Error(`target escapes alias root: ${target}`)\n  }\n}","typeGuard":"const isAliasTargetInside = (target: string, aliasRoot: string) => {\n  const m = target.match(/^@([^/]+)\\/(.+)$/)\n  if (!m) return true\n  const resolved = path.resolve(aliasRoot, m[2])\n  return resolved === aliasRoot || resolved.startsWith(aliasRoot + path.sep)\n}","tryCatchPattern":"try {\n  await updateFiles(tree, config, options)\n} catch (e) {\n  if (e instanceof Error && /must stay within the .* alias root/.test(e.message)) {\n    // rewrite the target to remove \"..\" and retry\n  }\n  throw e\n}","preventionTips":["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."],"tags":["paths","aliases","path-traversal","update"],"backgroundTag":null,"analyzedSha":"efac5987074af84ece57c367c6dd83387b967022","analyzedAt":"2026-08-12T05:00:50.218Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}