{"record":{"id":"f2ea80451033c1b8","repo":"thedotmack/claude-mem","slug":"unable-to-acquire-chroma-writer-lock-at-lockpath","errorCode":null,"errorMessage":"Unable to acquire Chroma writer lock at ${lockPath}: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Unable to acquire Chroma writer lock at (.+?): (.+?)","errorType":"exception","errorClass":"ChromaUnavailableError","httpStatus":null,"severity":"error","filePath":"src/services/sync/ChromaMcpManager.ts","lineNumber":418,"sourceCode":"      acquiredAt: new Date().toISOString(),\n      startToken: captureProcessStartToken(process.pid),\n    };\n\n    for (let attempt = 0; attempt < 2; attempt += 1) {\n      try {\n        fs.writeFileSync(lockPath, JSON.stringify(payload, null, 2), {\n          encoding: 'utf-8',\n          flag: 'wx',\n        });\n        this.chromaWriterLock = { path: lockPath, dataDir: normalizedDataDir, ownerId: this.chromaWriterOwnerId };\n        logger.debug('CHROMA_MCP', 'Acquired Chroma writer lock', { lockPath, dataDir: normalizedDataDir });\n        return;\n      } catch (error) {\n        const errno = error instanceof Error ? (error as NodeJS.ErrnoException).code : undefined;\n        if (errno !== 'EEXIST') {\n          const message = `Unable to acquire Chroma writer lock at ${lockPath}: ${error instanceof Error ? error.message : String(error)}`;\n          recordChromaVectorSearchUnavailable(message);\n          throw new ChromaUnavailableError(message, error instanceof Error ? error : undefined);\n        }\n\n        const existing = ChromaMcpManager.readChromaWriterLock(lockPath);\n        if (!existing) {\n          const message = `Chroma writer lock at ${lockPath} is unreadable; refusing to start a second writer`;\n          recordChromaVectorSearchUnavailable(message);\n          throw new ChromaUnavailableError(message);\n        }\n\n        if (existing.pid === process.pid && existing.ownerId === this.chromaWriterOwnerId) {\n          this.chromaWriterLock = { path: lockPath, dataDir: normalizedDataDir, ownerId: this.chromaWriterOwnerId };\n          return;\n        }\n\n        if (!ChromaMcpManager.isChromaWriterLockLive(existing)) {\n          try {\n            fs.rmSync(lockPath, { force: true });\n            logger.info('CHROMA_MCP', 'Removed stale Chroma writer lock', {","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/services/sync/ChromaMcpManager.ts#L400-L436","documentation":"To prevent two processes writing the same local Chroma data dir simultaneously (SQLite-like corruption risk), acquireChromaWriterLock() atomically creates a .claude-mem-chroma-writer.lock via fs.writeFileSync flag 'wx'. If writeFileSync throws anything OTHER than EEXIST (e.g. EACCES, ENOSPC, ENOENT dir missing, EROFS), it records health and throws ChromaUnavailableError with the underlying message. EEXIST is handled separately by inspecting the existing lock.","triggerScenarios":"The atomic lock create fails with a non-EEXIST errno: no write permission (EACCES) on the data dir, read-only filesystem (EROFS), disk full (ENOSPC), the data dir was deleted between mkdirSync and the write (ENOENT), or a path/encoding error.","commonSituations":"Data dir on a read-only mount or owned by another user; disk full; SELinux/AppArmor denying writes; the chroma data dir is on a network FS that rejects exclusive create; a prior crash left filesystem in a bad state.","solutions":["Check the underlying message/errno in the error: fix the filesystem permission or space issue on the Chroma data dir.","Ensure the user running the worker owns/can write the local Chroma data dir.","Free disk space if ENOSPC; remount read-write if EROFS; relax MAC policy if it blocks the write.","Retry the operation once the filesystem issue is resolved."],"exampleFix":"# before\nls -la ~/.claude-mem/chroma/  # owned by root, worker runs as appuser\n# acquireChromaWriterLock -> EACCES -> throws\n\n# after\nsudo chown -R appuser:appuser ~/.claude-mem/chroma\n# restart worker","handlingStrategy":"try-catch","validationCode":"import { access, constants } from 'fs/promises';\nimport path from 'path';\n\nasync function canWriteLock(dataDir: string): Promise<boolean> {\n  try {\n    await access(path.dirname(dataDir), constants.W_OK);\n    return true;\n  } catch { return false; }\n}","typeGuard":"import { ChromaUnavailableError } from '../worker/search/errors.js';\n\nfunction isWriterLockAcquireFailure(e: unknown): boolean {\n  return e instanceof ChromaUnavailableError && /Unable to acquire Chroma writer lock/i.test(e.message);\n}","tryCatchPattern":"try {\n  await chromaManager.search(query);\n} catch (e) {\n  if (e instanceof ChromaUnavailableError && /Unable to acquire Chroma writer lock/i.test(e.message)) {\n    // Filesystem-level issue (perms/space). Surface actionable guidance, degrade to FTS.\n    logger.error('CHROMA', e.message, {}, e);\n    return await ftsSearch(query);\n  }\n  throw e;\n}","preventionTips":["Run the worker as a user that owns/can write the Chroma data dir.","Keep the data dir on a writable, non-read-only filesystem with free space.","Monitor disk usage to avoid ENOSPC during embedding writes."],"tags":["chroma","filesystem","permissions","lock","vector-search","disk"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}