thedotmack/claude-mem · warning
[uninstall] cleanup failed
Error message
[uninstall] ${label} cleanup failed: What it means
runUninstallCommand runs each post-worker cleanup step through a shared helper that awaits fn() and reports success ('removed.') when it returns 0. If a cleanup function throws, the helper logs this warning prefixed with the step's label and uninstall continues with the remaining steps and the final note. This is the generic guard so one failing cleanup stage never aborts the whole uninstall.
Solutions
- Read the <label> in the warning to identify which cleanup step failed, then fix that specific path's permissions and re-run uninstall.
- Remove the leftover artifacts manually (paths are printed by the failing step's own warnings).
- Stop the worker and close Claude Code sessions first, then re-run uninstall so no files are locked.
- Run the uninstall as the same user that installed the plugin; avoid sudo for user-level installs.
Example fix
// before (files locked -> cleanup step throws)
await runCleanupStep('Settings', cleanupFn); // [uninstall] Settings cleanup failed
// after (close Claude first)
// pkill -f claude; then re-run
await runCleanupStep('Settings', cleanupFn); // Settings: removed. Defensive patterns
Strategy: try-catch
Validate before calling
// Before uninstall, confirm ownership of all target roots:
for (const dir of ['~/.claude-mem', '~/.claude/plugins', '~/.cache/claude-cli-nodejs', '~/.npm/_npx']) {
try { accessSync(expand(dir), R_OK | W_OK); } catch { console.warn(`${dir} not writable by current user`); }
} Type guard
function isCleanupError(e: unknown): e is NodeJS.ErrnoException {
return e instanceof Error && 'code' in e && typeof (e as NodeJS.ErrnoException).code === 'string';
} Try / catch
try {
const result = await fn();
if (result === 0) p.log.info(`${label}: removed.`);
} catch (e) {
console.warn(`[uninstall] ${label} cleanup failed (${isCleanupError(e) ? e.code : e}) — continuing; re-run after fixing`);
} Prevention
- Close Claude Code sessions and stop the worker before uninstalling to avoid locked files.
- Install and uninstall as the same (non-root) user for consistent file ownership.
- Re-run uninstall after a partial failure — steps are idempotent and skip already-removed paths.
- Read the <label> in the warning to target fixes at the exact failing step instead of guessing.
When it happens
Trigger: Any wrapped cleanup fn() throws — e.g. filesystem helpers hitting EACCES/EBUSY/ENOENT mid-operation, JSON settings rewriting failing, or registry/config removal APIs raising — inside the try around await fn().
Common situations: Mixed-permission ~/.claude trees after running Claude with sudo; locked plugin files on Windows; partially-corrupted installs where a cleanup step assumes files that no longer exist; concurrent uninstall sessions racing on the same paths.
Related errors
- claude-mem does not appear to be installed.
- [uninstall] Could not read settings for server runtime…
- [uninstall] Could not rewrite
- Error processing
- Failed to clean AGENTS.md context
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/99ad66a47269172f.
Report an issue: GitHub.
Appendix: source
Thrown at src/npx-cli/commands/uninstall.ts:390
}},
{ label: 'Codex CLI', fn: async () => {
const { uninstallCodexCli } = await import('../../services/integrations/CodexCliInstaller.js');
return uninstallCodexCli();
}},
{ label: 'Antigravity CLI hooks + MCP', fn: async () => {
const { uninstallAntigravityCliHooks } = await import('../../services/integrations/AntigravityCliHooksInstaller.js');
return uninstallAntigravityCliHooks();
}},
];
for (const { label, fn } of ideCleanups) {
try {
const result = await fn();
if (result === 0) {
p.log.info(`${label}: removed.`);
}
} catch (error: unknown) {
console.warn(`[uninstall] ${label} cleanup failed:`, error instanceof Error ? error.message : String(error));
}
}
p.note(
[
`Your data directory at ${styleText('cyan', '~/.claude-mem')} was preserved.`,
'To remove it manually: rm -rf ~/.claude-mem',
].join('\n'),
'Note',
);
// Capture BEFORE the data dir note becomes stale advice: consent and the
// install ID still live in ~/.claude-mem, which uninstall preserves.
await captureCliEvent('uninstall_completed', {}, { person: true });
p.outro(styleText('green', 'claude-mem has been uninstalled.'));
}
View on GitHub (pinned to d8bc9755e7)