affaan-m/ECC · error

Nasiko installation requires explicit --yes consent.

Error message

Nasiko installation requires explicit --yes consent.

What it means

installNasiko performs no filesystem changes during a dry run, but a real installation mutates the install directory and downloads a binary. To prevent accidental installs, the library requires options.yes to be explicitly truthy; otherwise it throws this consent error before creating anything.

Solutions

  1. Pass yes: true in the options object when you intend a real install.
  2. On the CLI, add the --yes flag to the install command.
  3. Keep dryRun true while previewing; flip it off together with adding yes: true.
  4. Audit wrapper scripts to ensure the consent flag is forwarded to installNasiko.

Example fix

// before
await installNasiko({ version: 'v0.1.0', installDir: '/usr/local/bin' }) // throws
// after
await installNasiko({ version: 'v0.1.0', installDir: '/usr/local/bin', yes: true })
Defensive patterns

Strategy: validation

Validate before calling

function assertInstallConsent(options) {
  if (!options.dryRun && !options.yes) {
    throw new Error('real install requires { yes: true } or the --yes CLI flag')
  }
}
assertInstallConsent(myOptions)
await installNasiko(myOptions)

Type guard

const hasInstallConsent = (o) => Boolean(o && o.yes) || Boolean(o && o.dryRun)

Try / catch

try {
  await installNasiko(opts)
} catch (e) {
  if (e.message.includes('explicit --yes consent')) {
    return installNasiko({ ...opts, yes: await promptForConfirmation() })
  }
  throw e
}

Prevention

When it happens

Trigger: Calling installNasiko({ version, installDir, dryRun: false }) — or omitting dryRun — without options.yes: true; CLI paths that failed to forward the --yes flag into the options object.

Common situations: Scripting the library API after testing with dryRun (dryRun returns the plan and never reaches the consent check); forgetting to pass --yes through a wrapper script; interactive prompts skipped in CI.

Understand the failure class

Background: "--flag is required" and "must specify" CLI errors: how missing-required-flag validation works and how to fix it — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

  try {
    return createLifecycleLock(lockPath, fileSystem);
  } catch (error) {
    if (error.code === 'EEXIST') {
      throw new Error(`Another Nasiko lifecycle operation won stale-lock recovery: ${lockPath}.`);
    }
    throw error;
  }
}

async function installNasiko(options = {}, dependencies = {}) {
  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.');

View on GitHub (pinned to 8321021c54)