{"record":{"id":"26dfab53b890d5a5","repo":"affaan-m/ECC","slug":"another-nasiko-lifecycle-operation-won-lock-acquisition","errorCode":null,"errorMessage":"Another Nasiko lifecycle operation won lock acquisition: ${lockPath}.","messagePattern":"Another Nasiko lifecycle operation won lock acquisition: (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"scripts/lib/nasiko-release.js","lineNumber":355,"sourceCode":"  };\n}\n\nfunction acquireLifecycleLock(installDirectory, fileSystem = fs, options = {}) {\n  const lockPath = path.join(installDirectory, '.ecc-nasiko-lifecycle.lock');\n  try {\n    return createLifecycleLock(lockPath, fileSystem);\n  } catch (error) {\n    if (error.code !== 'EEXIST') throw error;\n  }\n\n  let existing;\n  try { existing = inspectLifecycleLock(lockPath, fileSystem); }\n  catch (error) {\n    if (error.code === 'ENOENT') {\n      try { return createLifecycleLock(lockPath, fileSystem); }\n      catch (retryError) {\n        if (retryError.code === 'EEXIST') {\n          throw new Error(`Another Nasiko lifecycle operation won lock acquisition: ${lockPath}.`);\n        }\n        throw retryError;\n      }\n    }\n    throw error;\n  }\n  const isProcessAlive = options.isProcessAlive || processIsAlive;\n  if (!existing || isProcessAlive(existing.metadata.pid)) {\n    throw new Error(`Another Nasiko lifecycle operation is already in progress; inspect ${lockPath} before recovering a stale lock.`);\n  }\n  if (!removeLockIfOwned(lockPath, existing.stats, fileSystem)) {\n    throw new Error(`Nasiko lifecycle lock changed during stale-owner recovery: ${lockPath}.`);\n  }\n  try {\n    return createLifecycleLock(lockPath, fileSystem);\n  } catch (error) {\n    if (error.code === 'EEXIST') {\n      throw new Error(`Another Nasiko lifecycle operation won stale-lock recovery: ${lockPath}.`);","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/nasiko-release.js#L337-L373","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ninstallNasiko({ yes: true }) // throws in parallel CI\n// after\ntry {\n  installNasiko({ yes: true })\n} catch (e) {\n  if (String(e.message).includes('won lock acquisition')) await sleep(1000), installNasiko({ yes: true })\n  else throw e\n}","handlingStrategy":"retry","validationCode":"// Before install, check for a live lock owner:\nconst lockPath = path.join(installDir, '.ecc-nasiko-lifecycle.lock')\nif (fs.existsSync(lockPath)) {\n  const { pid } = JSON.parse(fs.readFileSync(lockPath, 'utf8'))\n  try { process.kill(pid, 0); throw new Error(`operation ${pid} still running`) } catch (e) { if (e.code !== 'ESRCH') throw e }\n}","typeGuard":null,"tryCatchPattern":"async function installWithRetry(opts, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try { return await installNasiko(opts) }\n    catch (e) {\n      if (!/won lock acquisition|changed during/.test(e.message) || i === attempts - 1) throw e\n      await new Promise(r => setTimeout(r, 500 * 2 ** i))\n    }\n  }\n}","preventionTips":["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."],"tags":["concurrency","locking","race-condition"],"backgroundTag":"address-already-in-use","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}