{"record":{"id":"3a4b89fd981d9aa7","repo":"thedotmack/claude-mem","slug":"chroma-writer-lock-at-lockpath-is-unreadable-refusing-to","errorCode":null,"errorMessage":"Chroma writer lock at ${lockPath} is unreadable; refusing to start a second writer","messagePattern":"Chroma writer lock at (.+?) is unreadable; refusing to start a second writer","errorType":"exception","errorClass":"ChromaUnavailableError","httpStatus":null,"severity":"error","filePath":"src/services/sync/ChromaMcpManager.ts","lineNumber":506,"sourceCode":"        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          }\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', {\n              lockPath,\n              priorPid: existing.pid,\n              priorStartedAt: existing.acquiredAt,\n            });\n            continue;\n          } catch (removeError) {\n            const message = `Unable to remove stale Chroma writer lock at ${lockPath}: ${removeError instanceof Error ? removeError.message : String(removeError)}`;","sourceCodeStart":488,"sourceCodeEnd":524,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/ChromaMcpManager.ts#L488-L524","documentation":"Thrown by acquireChromaWriterLock when the Chroma writer lock file exists (writeFileSync with flag 'wx' hit EEXIST) but its contents cannot be parsed into a valid ChromaWriterLockPayload. Since liveness/staleness cannot be determined without a readable owner, the manager refuses to start a second Chroma writer to avoid corrupting the shared data dir, marks vector search unavailable, and throws ChromaUnavailableError.","triggerScenarios":"Another process created the lock file but it is corrupt, empty, partially written (crash mid-write), or written in an unexpected format, and it is still within the abandoned-lock grace period (isChromaWriterLockAbandoned returned false).","commonSituations":"A previous Claude-mem process was killed (SIGKILL/power loss) while serializing the lock JSON; manual editing of files under ~/.claude-mem/chroma; disk-full leaving a truncated lock file; an older plugin version wrote a lock payload with a different schema.","solutions":["Inspect the lock file in the Chroma data dir (a writer.lock-style file) and confirm it is valid JSON with pid/ownerId/dataDir/acquiredAt fields","Delete the corrupt lock file manually once no chroma-mcp process is running, then retry the connection","Wait past the abandoned-lock grace period and retry - the manager reaps unreadable locks once they are old enough","Check for disk-full (ENOSPC) or permission issues that could have caused a truncated lock write"],"exampleFix":"// before: blind retry fails because the corrupt lock is within grace period\nawait manager.connect();\n// after: remove the corrupt lock when no chroma process is alive\nimport fs from 'fs';\nconst lockPath = `${dataDir}/writer.lock`;\nconst raw = fs.readFileSync(lockPath, 'utf-8');\ntry { JSON.parse(raw); } catch {\n  if (!isChromaRunning()) fs.rmSync(lockPath, { force: true });\n}\nawait manager.connect();","handlingStrategy":"try-catch","validationCode":"import fs from 'fs';\nfunction isChromaWriterLockSane(lockPath: string): boolean {\n  try {\n    const p = JSON.parse(fs.readFileSync(lockPath, 'utf-8'));\n    return typeof p.pid === 'number' && typeof p.ownerId === 'string';\n  } catch { return false; }\n}\nconst lockPath = `${dataDir}/writer.lock`;\nif (fs.existsSync(lockPath) && !isChromaWriterLockSane(lockPath)) {\n  // let the abandoned-lock grace period pass or clean up before connect\n}","typeGuard":"function isChromaUnavailableError(e: unknown): e is ChromaUnavailableError {\n  return e instanceof ChromaUnavailableError;\n}","tryCatchPattern":"try {\n  await manager.connect();\n} catch (e) {\n  if (e instanceof ChromaUnavailableError && /unreadable; refusing to start a second writer/.test(e.message)) {\n    // wait out grace period, clean corrupt lock if no chroma process alive, retry once\n    if (!isChromaProcessAlive()) fs.rmSync(lockPath, { force: true });\n    await manager.connect();\n  } else throw e;\n}","preventionTips":["Avoid killing processes with SIGKILL while chroma-mcp startup is in flight; prefer graceful shutdown so locks are released","Never hand-edit files in the chroma data dir, especially lock files","Monitor disk space on the volume hosting the chroma data dir","Catch ChromaUnavailableError and degrade gracefully - vector sync is optional, the plugin still works without it"],"tags":["chroma","file-lock","corrupt-lock-file","writer-lock"],"backgroundTag":"json-parse-error","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"}