pnpm/pnpm · warning · AggregateError
Failed to clean up replaced global installs
Error message
Failed to clean up replaced global installs
What it means
cleanupReplacedGlobalInstalls runs after activation already succeeded, removing installs that this activation replaced. Every group's removal is attempted even after failures; with exactly one failure that error is rethrown as-is, and with several the errors are wrapped in an AggregateError ('Failed to clean up replaced global installs'). The new install is live — only old directories or bins remain on disk.
Source
Thrown at pnpm11/global/commands/src/globalActivation.ts:95
// Activation is already committed, so a leftover backup directory
// must not fail the command — but it points at a filesystem problem
// worth surfacing.
globalWarn(`Failed to remove the global bin backup directory at ${prepared.backupDir}: ${getErrorMessage(err)}`)
}
return prepared.actualBinNames
}
export async function cleanupReplacedGlobalInstalls (
opts: CleanupReplacedGlobalInstallsOptions
): Promise<void> {
const errors: unknown[] = []
for (const group of opts.groups) {
// eslint-disable-next-line no-await-in-loop -- Cleanup mutations must settle before the next group starts.
errors.push(...await cleanupReplacedGlobalInstall(opts, group))
}
if (errors.length === 1) throw errors[0]
if (errors.length > 1) {
throw new AggregateError(errors, 'Failed to clean up replaced global installs')
}
}
// Activation already succeeded when this runs, so every removal is
// attempted even after one fails; the failures are aggregated instead of
// aborting the remaining cleanup.
async function cleanupReplacedGlobalInstall (
opts: CleanupReplacedGlobalInstallsOptions,
group: GlobalPackageInfo
): Promise<unknown[]> {
const errors: unknown[] = []
let binNames: string[]
try {
binNames = await getInstalledBinNames(group)
} catch (err) {
// The install directory is the only record of which bins the group
// owns, so removing it now would strand them on PATH forever. Leave
// the group intact for a later run to clean up.View on GitHub (pinned to 5b11d3a15b)
Solutions
- Treat as non-fatal if the command you needed works: verify with `pnpm ls -g` and by running the binaries
- Close processes and terminals holding the old install paths, then re-run the global install so cleanup retries
- Delete the leftover replaced-install directories under the global package dir manually
Defensive patterns
Strategy: try-catch
Type guard
function isCleanupAggregate (err: unknown): err is AggregateError {
return util.types.isNativeError(err) && Array.isArray((err as AggregateError).errors)
} Try / catch
try {
await activateAndCleanup(opts)
} catch (err) {
if (isCleanupAggregate(err) && err.message === 'Failed to clean up replaced global installs') {
// activation already succeeded: log err.errors and continue — verify bins run, clean leftovers later
for (const e of err.errors) log.warn('cleanup failure:', e)
return
}
throw err
} Prevention
- Close shells and processes using global bins before updating global packages
- Periodically prune the global dir of orphaned old installs
- Treat cleanup AggregateErrors as warnings in automation, not install failures
When it happens
Trigger: Post-activation removal of a replaced install directory or old bin fails: EBUSY/EPERM from locked files on Windows, read-only files, a terminal whose cwd is inside the removed directory, or a process holding an old shim open.
Common situations: Windows file locks on old shims during global update; editors or shells sitting inside the old install dir; leftovers from previous installs with unusual permissions.
Related errors
- Failed to clean up after global bin activation preparation f
- Failed to clean up after global bin activation failed.${rema
- GLOBAL_BIN_ROLLBACK_FAILED
- GLOBAL_BIN_UNSUPPORTED_TYPE
- Failed to remove Windows bin shims for ${cmd}
AI-assisted analysis of pnpm/pnpm@5b11d3a15b (2026-08-16).
Data as JSON: /api/errors/cb535421cca59ac5.
Report an issue: GitHub.