affaan-m/ECC · error
Refusing to remove an unqualified or modified Nasiko…
Error message
Refusing to remove an unqualified or modified Nasiko executable.
What it means
During uninstall, the existing executable at the destination is inspected and must be a qualified Nasiko binary of exactly the requested version; otherwise the installer refuses to delete it. This protects against removing user-modified, corrupted, or unrelated files that happen to occupy the install path.
Solutions
- Pass the exact version currently installed (check the receipt/metadata) instead of assuming a different version.
- Reinstall the matching qualified version first if the binary was corrupted, then uninstall cleanly.
- Manually remove the destination file and its metadata receipt if you are certain the file is safe to delete.
- Verify no post-install process modified the binary (permissions, patches, antivirus quarantine) before retrying.
Example fix
// before
await uninstallNasiko({ version: 'v0.2.0', yes: true }); // throws: destination holds v0.1.0
// after
const status = inspectInstalledNasiko(destination);
await uninstallNasiko({ version: status.version, yes: true }); Defensive patterns
Strategy: validation
Validate before calling
const status = inspectInstalledNasiko(destination);
if (!status.installed || !status.qualified || status.version !== targetVersion) {
throw new Error(`cannot uninstall: installed=${status.installed} qualified=${status.qualified} version=${status.version}`);
} Type guard
function isQualifiedVersion(status, version) {
return Boolean(status) && status.installed === true && status.qualified === true && status.version === version;
} Try / catch
try {
await uninstallNasiko({ version, yes: true });
} catch (e) {
if (e.message.includes('unqualified or modified')) {
// inspect receipt, then uninstall the actual installed version or remove manually
} else throw e;
} Prevention
- Track the installed version and uninstall exactly that version
- Do not modify, chmod-strip, or patch the binary after install
- Inspect the receipt metadata before uninstalling
- Only remove destination files manually when you are certain they are Nasiko artifacts
When it happens
Trigger: inspectInstalledNasiko(destination) reports installed=true but status.qualified is false, or status.version !== the version passed to uninstallNasiko.
Common situations: Trying to uninstall a version that was already replaced by an upgrade; the binary was modified/patched after install so qualification fails; a non-Nasiko file occupies the destination path; a partial/corrupted install.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Codex returned no final response
- Missing merge payload for uninstall
- Nasiko uninstall requires explicit --yes consent.
- Refusing to outside the install root: ' ' is not within ' '.
- Refusing to through symlinked Claude skill path: ' '.
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/1d9343b3e1d150bc.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/nasiko-release.js:450
const destination = path.join(installDirectory, release.binaryName);
const plan = { dryRun: Boolean(options.dryRun), version, destination };
if (options.dryRun) return plan;
if (!options.yes) throw new Error('Nasiko uninstall requires explicit --yes consent.');
if (!fs.existsSync(installDirectory)) return { ...plan, dryRun: false, removed: false };
assertPrivateInstallDirectory(installDirectory);
const releaseLock = acquireLifecycleLock(installDirectory);
const metadataPath = metadataPathFor(destination);
const suffix = `${process.pid}-${crypto.randomBytes(6).toString('hex')}`;
const binaryTombstone = `${destination}.remove-${suffix}`;
const metadataTombstone = `${metadataPath}.remove-${suffix}`;
let binaryStaged = false;
let metadataStaged = false;
const rename = dependencies.rename || fs.renameSync;
const remove = dependencies.remove || (target => fs.rmSync(target));
try {
const status = (dependencies.inspectInstalled || inspectInstalledNasiko)(destination);
if (!status.installed) return { ...plan, dryRun: false, removed: false };
if (!status.qualified || status.version !== version) throw new Error('Refusing to remove an unqualified or modified Nasiko executable.');
rename(destination, binaryTombstone);
binaryStaged = true;
rename(metadataPath, metadataTombstone);
metadataStaged = true;
const cleanupPending = [];
try { remove(metadataTombstone); } catch (_error) { cleanupPending.push(metadataTombstone); }
metadataStaged = false;
try { remove(binaryTombstone); } catch (_error) { cleanupPending.push(binaryTombstone); }
binaryStaged = false;
if (cleanupPending.length > 0) {
const cleanupError = new Error(`Nasiko uninstall is incomplete; retained staged file(s): ${cleanupPending.join(', ')}. Remove these files before reinstalling.`);
cleanupError.cleanupPending = cleanupPending;
throw cleanupError;
}
return { ...plan, dryRun: false, removed: true, cleanupPending: [] };
} catch (error) {
if (metadataStaged && !fs.existsSync(metadataPath)) rename(metadataTombstone, metadataPath);
if (binaryStaged && !fs.existsSync(destination)) rename(binaryTombstone, destination);View on GitHub (pinned to 8321021c54)