{"record":{"id":"f2ea80451033c1b8","repo":"thedotmack/claude-mem","slug":"unable-to-acquire-chroma-writer-lock-at-lockpath-error","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":485,"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          // Without a readable owner the liveness reaper below can never run,\n          // so vector sync would stay dead until someone deletes the file (#3916).\n          // Treat an unreadable lock that is past the write grace period as stale.\n          if (ChromaMcpManager.isChromaWriterLockAbandoned(lockPath)) {\n            try {\n              fs.rmSync(lockPath, { force: true });\n              logger.info('CHROMA_MCP', 'Removed unreadable Chroma writer lock', { lockPath });\n              continue;\n            } catch (removeError) {\n              const message = `Unable to remove unreadable Chroma writer lock at ${lockPath}: ${removeError instanceof Error ? removeError.message : String(removeError)}`;\n              recordChromaVectorSearchUnavailable(message);\n              throw new ChromaUnavailableError(message, removeError instanceof Error ? removeError : undefined);\n            }\n          }","sourceCodeStart":467,"sourceCodeEnd":503,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/ChromaMcpManager.ts#L467-L503","documentation":"acquireChromaWriterLock creates a lockfile (O_EXCL-style) to ensure only one chroma-mcp writer runs. If creation fails with an errno other than EEXIST (e.g. EACCES, ENOSPC, EPERM), the lock cannot be managed at all, so it records unavailability and throws ChromaUnavailableError with the lock path and cause.","triggerScenarios":"connectInternal attempting to acquire the writer lock when the lock directory is missing or read-only, the filesystem denies writes (permissions, read-only mount, sandbox), the disk is full, or a path-level error other than 'file already exists' occurs.","commonSituations":"Running under a restricted user or container without write access to the lock path (often under ~/.claude-mem or the data dir); HOME misconfigured so the lock path is invalid; read-only rootfs in Docker; disk quota exhausted.","solutions":["Check the errno/cause in the message; verify the lock file's parent directory exists and is writable by the process user.","Fix permissions (chown/chmod) or point the data/lock directory to a writable location via settings.","Free disk space / quota if ENOSPC.","If running in a container, mount the data directory as a writable volume."],"exampleFix":"// before (container)\n# no volume -> read-only lock dir\n// after\ndocker run -v claude-mem-data:/root/.claude-mem image","handlingStrategy":"try-catch","validationCode":"import fs from 'fs';\nconst lockDir = path.dirname(lockPath);\nfs.mkdirSync(lockDir, { recursive: true });\nfs.accessSync(lockDir, fs.constants.W_OK); // throws early if not writable","typeGuard":null,"tryCatchPattern":"try {\n  await manager.ensureConnected();\n} catch (e) {\n  if (e instanceof ChromaUnavailableError && e.message.includes('Unable to acquire Chroma writer lock')) {\n    logger.error('Fix lock dir permissions/disk space:', { cause: e.cause ?? e.message });\n    disableVectorSearchLocally();\n  } else throw e;\n}","preventionTips":["Run the app as a user with write access to the data/lock directory.","Mount data directories as writable volumes in containers.","Monitor disk space/quota on the partition holding ~/.claude-mem.","Check the lock parent directory exists and is writable at startup."],"tags":["chroma","filesystem","lock","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}