{"record":{"id":"14b6107e860df40e","repo":"affaan-m/ECC","slug":"another-ecc-process-is-updating-claude-settings-settingspath","errorCode":null,"errorMessage":"Another ECC process is updating Claude settings: ${settingsPath}. If no ECC process is active, inspect and remove ${lockPath}.","messagePattern":"Another ECC process is updating Claude settings: (.+?)\\. If no ECC process is active, inspect and remove (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/claude-settings-lock.js","lineNumber":145,"sourceCode":"  } finally {\n    fs.rmSync(recoveryPath, { recursive: true, force: true });\n    fs.rmSync(quarantinePath, { force: true });\n  }\n}\n\nfunction acquireSettingsLock(settingsPath) {\n  const lockPath = `${settingsPath}.ecc.lock`;\n  fs.mkdirSync(path.dirname(settingsPath), { recursive: true });\n  try {\n    return createSettingsLock(lockPath);\n  } catch (error) {\n    if (!error || error.code !== 'EEXIST') {\n      throw error;\n    }\n  }\n  const recovered = recoverSettingsLock(lockPath);\n  if (recovered) return recovered;\n  throw new Error(\n    `Another ECC process is updating Claude settings: ${settingsPath}. `\n    + `If no ECC process is active, inspect and remove ${lockPath}.`\n  );\n}\n\nfunction runWithSettingsLock(settingsPath, callback) {\n  const releaseLock = acquireSettingsLock(settingsPath);\n  let primaryError = null;\n  let result;\n  try {\n    result = callback();\n  } catch (error) {\n    primaryError = error;\n  }\n\n  let releaseError = null;\n  try {\n    releaseLock();","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/claude-settings-lock.js#L127-L163","documentation":"acquireSettingsLock tries to create the lock file exclusively; on EEXIST it attempts recovery of a stale lock, and if recovery also fails it concludes another ECC process holds the settings lock. It aborts with instructions pointing at both the settings path and the lock file so the user can verify no process is running and manually clean up.","triggerScenarios":"Calling any settings-mutating ECC command while another ECC process holds the lock (exclusive create returns EEXIST), and recoverSettingsLock cannot determine the lock is stale (holder still alive or lock identity ambiguous).","commonSituations":"Two installers run concurrently (terminal + IDE automation); a previous ECC process crashed leaving a lock and the PID check cannot safely recover it; container environments where PID reuse confuses staleness detection.","solutions":["Wait for the running ECC process to finish, or kill it if it is hung.","Verify no ECC process is active (ps), then manually remove the lock file shown in the message and retry.","Serialize ECC invocations (don't run install/uninstall concurrently in scripts).","In containers, ensure each container has a single ECC process and clean lock files on restart."],"exampleFix":"// before (concurrent)\nrun('ecc install') ; run('ecc uninstall') // parallel\n// after\nawait run('ecc install'); await run('ecc uninstall') // sequential","handlingStrategy":"retry","validationCode":"// guard before invoking ECC settings operations\nimport { existsSync } from 'fs';\nconst lockPath = settingsPath + '.lock';\nif (existsSync(lockPath)) {\n  const running = execSync('pgrep -f ecc-install || true').toString().trim();\n  if (running) throw new Error('Another ECC process holds the settings lock');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await withSettingsLock(settingsPath, work);\n} catch (e) {\n  if (e.message.includes('Another ECC process is updating Claude settings')) {\n    await wait(2000);\n    return acquireSettingsLock(settingsPath); // bounded retry\n  } else if (e.message.includes('inspect and remove')) {\n    // no active process path: remove stale lock, then retry once\n  } else throw e;\n}","preventionTips":["Serialize all ECC install/uninstall invocations in scripts.","Use a process manager to prevent orphaned ECC processes holding locks.","Clean up lock files on container restart when no process could survive."],"tags":["locking","concurrency","installer"],"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"}