shadcn-ui/ui · warning
Could not remove ${removablePackages.join( ", "
Error message
Could not remove ${removablePackages.join(
", "
)}. Remove them manually after reviewing the migration. What it means
After a successful migration, migrateCn tries to uninstall the now-unused old packages (clsx, tailwind-merge, cnfast) via removeDependencies. That call runs a package-manager command which can fail; the CLI catches the failure and only logs a warning telling you to remove the packages manually — the migration itself is not rolled back or marked failed.
Source
Thrown at packages/shadcn/src/migrations/cn/index.ts:108
`Migrated ${changedFiles.length} file${changedFiles.length === 1 ? "" : "s"}.`
)
const retainedPackages = new Set(
results.flatMap(({ result }) => result.remainingPackages)
)
const removablePackages = options.path
? []
: OLD_PACKAGES.filter(
(packageName) =>
migratedPackages.has(packageName) &&
!retainedPackages.has(packageName)
)
if (removablePackages.length) {
try {
await removeDependencies(options.cwd, removablePackages)
} catch {
logger.warn(
`Could not remove ${removablePackages.join(
", "
)}. Remove them manually after reviewing the migration.`
)
}
}
if (options.path) {
logger.info(
"Kept clsx, tailwind-merge and cnfast dependencies because this was a scoped migration."
)
}
printUnsupportedIssues(unsupported, options.cwd)
}
function assertTailwindCompatibility(
cwd: string,View on GitHub (pinned to 5c7072da67)
Solutions
- Manually uninstall the listed packages: `npm uninstall clsx tailwind-merge cnfast` (or your package manager's equivalent).
- Fix the underlying package-manager issue (install it, check network, fix permissions) and rerun the removal.
- Verify package.json and the lockfile afterwards to confirm the packages are gone and committed.
- Check node_modules write permissions if the failure was EACCES/EBUSY.
Example fix
// manual cleanup after the warning // before (package.json) "clsx": "^2.1.0", "tailwind-merge": "^3.0.0" // after: run `npm uninstall clsx tailwind-merge` "cn": "latest"
Defensive patterns
Strategy: try-catch
Try / catch
try {
await migrateCn({ cwd: process.cwd(), yes: true })
} catch {
// migration failures throw; dependency-removal failures only warn
} finally {
// verify cleanup
const pkg = JSON.parse(readFileSync('package.json', 'utf8'))
const stale = ['clsx', 'tailwind-merge', 'cnfast'].filter(p => pkg.dependencies?.[p])
if (stale.length) console.warn(`Remove manually: npm uninstall ${stale.join(' ')}`)
} Prevention
- Ensure the package manager (npm/pnpm/yarn) is installed and on PATH before migrating.
- Run migrations with network access so lockfile updates succeed.
- Check write permissions on package.json/node_modules in containers.
- After migrating, grep package.json for the old packages and uninstall leftovers.
When it happens
Trigger: removeDependencies throwing for any reason (package manager not installed, lockfile conflicts, no network, read-only node_modules, unsupported package manager) while OLD_PACKAGES members are eligible for removal in an unscoped (no --path) migration.
Common situations: Offline CI or air-gapped machines where the uninstall command cannot resolve the lockfile; pnpm/yarn/npm not on PATH in minimal containers; permission problems in globally-managed or Docker-owned directories.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- NOT_CONFIGURED
- Invalid dependency "${dep}": dependency names cannot start w
- The cn migration requires Tailwind CSS v4 and tailwind-merge
AI-assisted analysis of shadcn-ui/ui@5c7072da67 (2026-09-07).
Data as JSON: /api/errors/fbb9031dcbd6b24c.
Report an issue: GitHub.