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

  1. Manually uninstall the listed packages: `npm uninstall clsx tailwind-merge cnfast` (or your package manager's equivalent).
  2. Fix the underlying package-manager issue (install it, check network, fix permissions) and rerun the removal.
  3. Verify package.json and the lockfile afterwards to confirm the packages are gone and committed.
  4. 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

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


AI-assisted analysis of shadcn-ui/ui@5c7072da67 (2026-09-07). Data as JSON: /api/errors/fbb9031dcbd6b24c. Report an issue: GitHub.