affaan-m/ECC · error

An unqualified or incompatible Nasiko executable or receipt…

Error message

An unqualified or incompatible Nasiko executable or receipt already exists at the destination.

What it means

Thrown by installNasiko when the destination path already contains a Nasiko executable/receipt that is either not the qualified pinned build or whose recorded version differs from the requested version. The installer refuses to overwrite an existing file it cannot verify, so it aborts instead of clobbering. An exact version match that is qualified is silently reused.

Solutions

  1. Run the Nasiko uninstall flow (with --yes) for the destination to remove the unqualified or mismatched executable, then reinstall.
  2. Install the version already present at the destination (pass the existing version) so the installer takes the reuse path.
  3. Manually remove the destination file and its metadata receipt (metadataPathFor(destination)) and retry the install.
  4. Verify the binary was not modified after install (chmod/rename/truncate breaks qualification) and restore it from a clean source.

Example fix

// before
await installNasiko({ version: 'v0.2.0' }); // throws: v0.1.0 already at destination
// after
await uninstallNasiko({ version: 'v0.1.0', yes: true });
await installNasiko({ version: 'v0.2.0' });
Defensive patterns

Strategy: validation

Validate before calling

import { inspectInstalledNasiko } from './nasiko-release.js';
const status = inspectInstalledNasiko(destination);
if (status.installed && (!status.qualified || status.version !== desiredVersion)) {
  await uninstallNasiko({ version: status.version, yes: true });
}

Type guard

function isCleanDestination(destination, version) {
  const s = inspectInstalledNasiko(destination);
  return !s.installed || (s.qualified && s.version === version);
}

Try / catch

try {
  await installNasiko({ version });
} catch (e) {
  if (e.message.includes('already exists at the destination')) {
    await uninstallNasiko({ yes: true });
    await installNasiko({ version });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling installNasiko when inspectInstalledNasiko(destination).installed is true and either existing.qualified is false (file present but digest/qualification check failed) or existing.version !== version (different version already installed).

Common situations: A stale Nasiko binary left over from a previous install or manual download; upgrading Nasiko in place without uninstalling the old version first; a corrupted or user-modified binary that no longer passes qualification; reinstalling after a partial/unqualified bootstrap.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/65a87fb7f17f8af0. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/nasiko-release.js:398

  const version = options.version || 'v0.1.0';
  const base = getQualifiedRelease(version, dependencies.platform || process.platform, dependencies.arch || process.arch);
  const release = dependencies.releaseOverride ? { ...base, ...dependencies.releaseOverride } : base;
  const installDirectory = validateInstallDirectory(options.installDir || defaultInstallDirectory(release, dependencies.environment || process.env, dependencies.homeDirectory || os.homedir()));
  const destination = path.join(installDirectory, release.binaryName);
  const plan = { dryRun: Boolean(options.dryRun), version, platform: release.os, architecture: release.arch, manifestDigest: release.manifestDigest, binaryDigest: release.binaryDigest, registryOrigin: REGISTRY_ORIGIN, destination, license: release.license, sourceUrl: release.sourceUrl };
  if (options.dryRun) return plan;
  if (!options.yes) throw new Error('Nasiko installation requires explicit --yes consent.');
  fs.mkdirSync(installDirectory, { recursive: true, mode: 0o755 });
  assertPrivateInstallDirectory(installDirectory);
  const releaseLock = acquireLifecycleLock(installDirectory);
  const metadataPath = metadataPathFor(destination);
  let destinationOwned = false;
  let metadataOwned = false;
  try {
    const existing = inspectInstalledNasiko(destination);
    if (existing.installed) {
      if (existing.qualified && existing.version === version) return { ...plan, dryRun: false, installed: true, reused: true };
      throw new Error('An unqualified or incompatible Nasiko executable or receipt already exists at the destination.');
    }
    const retrieve = dependencies.fetchBytes || fetchBytes;
    const manifestBytes = await retrieve(`${REGISTRY_ORIGIN}/v2/${REPOSITORY}/manifests/${release.manifestDigest}`, { accept: 'application/vnd.oci.image.manifest.v1+json', maxBytes: MAX_MANIFEST_BYTES });
    assertDigest(manifestBytes, release.manifestDigest, 'Nasiko manifest');
    const layer = validateManifest(manifestBytes);
    const archiveBytes = await retrieve(`${REGISTRY_ORIGIN}/v2/${REPOSITORY}/blobs/${layer.digest}`, { maxBytes: MAX_ARCHIVE_BYTES });
    if (archiveBytes.length !== layer.size) throw new Error('Nasiko archive size mismatch.');
    assertDigest(archiveBytes, layer.digest, 'Nasiko archive');
    const binary = (dependencies.extractBinary || extractQualifiedTarGzip)(archiveBytes, release.binaryName);
    assertDigest(binary, release.binaryDigest, 'Nasiko binary');
    if (dependencies.beforePublish) dependencies.beforePublish(destination);
    const descriptor = fs.openSync(destination, 'wx', 0o700);
    destinationOwned = true;
    try {
      fs.writeFileSync(descriptor, binary);
      fs.fsyncSync(descriptor);
      if (fs.fstatSync(descriptor).size !== binary.length) throw new Error('Published Nasiko binary size mismatch.');
    } finally { fs.closeSync(descriptor); }

View on GitHub (pinned to 8321021c54)