{"record":{"id":"e6a18f04d4fae637","repo":"abhigyanpatwari/GitNexus","slug":"fts-reader-unrepairable","errorCode":"FTS_READER_UNREPAIRABLE","errorMessage":"Cannot open ${path.basename(dbPath)} read-only after an in-place FTS abort. The leftover WAL would replay and kill this process. Run `gitnexus analyze --repair-fts` after stopping any GitNexus MCP or serve process.","messagePattern":"Cannot open (.+?) read-only after an in-place FTS abort\\. The leftover WAL would replay and kill this process\\. Run `gitnexus analyze --repair-fts` after stopping any GitNexus MCP or serve process\\.","errorType":"exception","errorClass":"FtsReaderUnrepairableError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/lbug/sidecar-recovery.ts","lineNumber":77,"sourceCode":" * abort (live dirty flag, or a persisted in-place `native-abort`) and a\n * WAL is still live, refuse before the native open. Does not write,\n * rename, or repair. Parking still requires the conjunctive\n * `allowsFtsCrashWalPark` warrant. Missing or unreadable meta falls\n * through to today's open path.\n */\nexport const assertReadOnlyFtsCrashSafe = async (dbPath: string): Promise<void> => {\n  let meta;\n  try {\n    meta = await loadMeta(path.dirname(dbPath));\n  } catch {\n    return;\n  }\n  if (!meta || !shouldRefuseFtsCrashWal(meta.incrementalInProgress, meta.capabilities?.fts)) {\n    return;\n  }\n  const state = await inspectLbugSidecars(dbPath);\n  if (!sidecarHasLiveWal(state)) return;\n  throw new FtsReaderUnrepairableError(dbPath);\n};\n\nexport const ftsCrashParkFailureMessage = (failedPath: string, err?: unknown): string => {\n  const detail = err instanceof Error ? err.message : err != null ? String(err) : '';\n  return (\n    `Cannot park ${path.basename(failedPath)} after an in-place FTS abort` +\n    (detail ? ` (${detail})` : '') +\n    `. The database was not opened. Run \\`${CLEAN_LBUG_SIDECARS_COMMAND}\\` ` +\n    'after stopping any GitNexus MCP or serve process, then retry ' +\n    '`gitnexus analyze` or `gitnexus analyze --repair-fts`.'\n  );\n};\n\n/**\n * Counter-based warn anti-spam (PR #1747 review, Finding 6).\n *\n * The previous design (`warnedKeys: Set<string>`) warned exactly once per key\n * per process and silently downgraded all subsequent occurrences to debug. In","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/core/lbug/sidecar-recovery.ts#L59-L95","documentation":"assertReadOnlyFtsCrashSafe refuses to open the database read-only when sidecar metadata shows an incremental FTS checkpoint was in progress with FTS capabilities and a live WAL sidecar still exists on disk. Replaying that leftover WAL in a read-only open would crash the process, so it throws FtsReaderUnrepairableError (code FTS_READER_UNREPAIRABLE) and directs the user to --repair-fts.","triggerScenarios":"Opening the LadybugDB database read-only (e.g. by the MCP server or `gitnexus serve`) after a prior process aborted mid in-place FTS checkpoint, leaving a live .wal sidecar; inspectLbugSidecars confirms the WAL is still present.","commonSituations":"A crashed or SIGKILLed analyze run during an incremental FTS checkpoint; opening the repo with MCP/serve afterwards; running on a machine where the previous session terminated unexpectedly.","solutions":["Stop any GitNexus MCP or serve process holding the database.","Run `gitnexus analyze --repair-fts` to repair the FTS indexes and clean the leftover WAL.","If repair fails, run `gitnexus clean --lbug-sidecars` and then re-run `gitnexus analyze`."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { inspectLbugSidecars, sidecarHasLiveWal } from 'gitnexus/...';\nconst state = await inspectLbugSidecars(dbPath);\nif (sidecarHasLiveWal(state)) {\n  console.error('Leftover WAL from an FTS crash; run: gitnexus analyze --repair-fts');\n}","typeGuard":"const isFtsReaderUnrepairable = (e: unknown): e is FtsReaderUnrepairableError =>\n  e instanceof FtsReaderUnrepairableError;","tryCatchPattern":"try {\n  await openReadOnly(dbPath);\n} catch (err) {\n  if (isFtsReaderUnrepairable(err)) {\n    console.error('Run `gitnexus analyze --repair-fts` after stopping MCP/serve processes.');\n  } else throw err;\n}","preventionTips":["Avoid killing analyze processes mid-run (SIGKILL) — stop MCP/serve before forced shutdowns.","Run `gitnexus analyze --repair-fts` promptly after any crash during FTS checkpoints.","Ensure only one process opens the database at a time."],"tags":["database","wal","fts","crash-recovery"],"backgroundTag":"fts-crash-wal-leftover","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-09-15T23:29:44.066Z","contentChangedAt":"2026-09-15T23:29:44.066Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}