affaan-m/ECC · warning
Another Nasiko lifecycle operation won lock acquisition
Error message
Another Nasiko lifecycle operation won lock acquisition: ${lockPath}. What it means
During lock acquisition, when creating the lock file fails with EEXIST, the library inspects the existing lock. If the lock vanishes (ENOENT) between inspection and a re-create attempt that again hits EEXIST, another process has won the race in the interim; the library throws this error rather than retrying indefinitely. It signals benign but unresolved lock contention.
Solutions
- Re-run the lifecycle operation after the other process finishes (the error is transient contention).
- Serialize lifecycle operations in your tooling (a job queue or flock around the CLI call).
- Check the lock path listed in the message for a live owner (ps -p <pid> from the lock's pid field) and wait.
- Retry with backoff; this is a lost race, not a corrupt state.
Example fix
// before
installNasiko({ yes: true }) // throws in parallel CI
// after
try {
installNasiko({ yes: true })
} catch (e) {
if (String(e.message).includes('won lock acquisition')) await sleep(1000), installNasiko({ yes: true })
else throw e
} Defensive patterns
Strategy: retry
Validate before calling
// Before install, check for a live lock owner:
const lockPath = path.join(installDir, '.ecc-nasiko-lifecycle.lock')
if (fs.existsSync(lockPath)) {
const { pid } = JSON.parse(fs.readFileSync(lockPath, 'utf8'))
try { process.kill(pid, 0); throw new Error(`operation ${pid} still running`) } catch (e) { if (e.code !== 'ESRCH') throw e }
} Try / catch
async function installWithRetry(opts, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try { return await installNasiko(opts) }
catch (e) {
if (!/won lock acquisition|changed during/.test(e.message) || i === attempts - 1) throw e
await new Promise(r => setTimeout(r, 500 * 2 ** i))
}
}
} Prevention
- Serialize lifecycle operations with a CI concurrency group or flock.
- Avoid running install and uninstall concurrently on the same installDir.
- Treat 'won lock acquisition' errors as transient and retry with backoff.
When it happens
Trigger: Two or more installNasiko/uninstallNasiko processes racing on the same install directory: process A's lock is removed exactly while process B re-attempts createLifecycleLock, and B's exclusive create loses to a third contender.
Common situations: Parallel CI jobs installing nasiko into a shared directory; concurrent install and uninstall invocations in different terminals; a cron job and a manual install colliding.
Related errors
- Another Nasiko lifecycle operation won stale-lock recovery
- capsule.lock_lost
- Nasiko lifecycle lock changed during stale-owner recovery
- Another ECC process is updating Claude settings
- Another Nasiko lifecycle operation is already in progress…
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/26dfab53b890d5a5.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/nasiko-release.js:355
};
}
function acquireLifecycleLock(installDirectory, fileSystem = fs, options = {}) {
const lockPath = path.join(installDirectory, '.ecc-nasiko-lifecycle.lock');
try {
return createLifecycleLock(lockPath, fileSystem);
} catch (error) {
if (error.code !== 'EEXIST') throw error;
}
let existing;
try { existing = inspectLifecycleLock(lockPath, fileSystem); }
catch (error) {
if (error.code === 'ENOENT') {
try { return createLifecycleLock(lockPath, fileSystem); }
catch (retryError) {
if (retryError.code === 'EEXIST') {
throw new Error(`Another Nasiko lifecycle operation won lock acquisition: ${lockPath}.`);
}
throw retryError;
}
}
throw error;
}
const isProcessAlive = options.isProcessAlive || processIsAlive;
if (!existing || isProcessAlive(existing.metadata.pid)) {
throw new Error(`Another Nasiko lifecycle operation is already in progress; inspect ${lockPath} before recovering a stale lock.`);
}
if (!removeLockIfOwned(lockPath, existing.stats, fileSystem)) {
throw new Error(`Nasiko lifecycle lock changed during stale-owner recovery: ${lockPath}.`);
}
try {
return createLifecycleLock(lockPath, fileSystem);
} catch (error) {
if (error.code === 'EEXIST') {
throw new Error(`Another Nasiko lifecycle operation won stale-lock recovery: ${lockPath}.`);View on GitHub (pinned to 8321021c54)