{"record":{"id":"4fa4f4f9dfabe19d","repo":"abhigyanpatwari/GitNexus","slug":"cannot-start-dirty-state-recovery-the-interrupte","errorCode":null,"errorMessage":"Cannot start dirty-state recovery — the interrupted run's LadybugDB sidecars could neither be moved aside nor removed:","messagePattern":"Cannot start dirty-state recovery — the interrupted run's LadybugDB sidecars could neither be moved aside nor removed:","errorType":"exception","errorClass":"LbugWipeError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/run-analyze.ts","lineNumber":1398,"sourceCode":"          'from the interrupted run (the file could not be moved aside, so its bytes were ' +\n          'removed — post-mortem forensics lost). Recovery proceeds with full embedding ' +\n          'preservation.',\n      );\n    }\n    if (failed.length > 0) {\n      // FIX 1 (this shipping review, replacing the tri-review 4669518496\n      // P2-3 drop-shape design): under a persistent lock the old drop-shape\n      // run derived its embedding mode as \"drop\", ran the WHOLE pipeline,\n      // and then died at the rebuild wipe on the very same handle — wasting\n      // minutes and zeroing embeddings on the way. A possibly-poisoned\n      // sidecar still sits next to the DB (any pre-wipe open would replay it\n      // and die), so failing here, in seconds, with the same actionable\n      // typed error the wipe would eventually throw is strictly better —\n      // and the CLI's LbugWipeError handler already renders it\n      // (recoveryHint 'lbug-wipe-failed'). The message is self-contained\n      // (headline + paths + lock guidance) because serve forwards only\n      // err.message over worker IPC.\n      throw new LbugWipeError(failed, {\n        headline:\n          \"Cannot start dirty-state recovery — the interrupted run's LadybugDB sidecars \" +\n          'could neither be moved aside nor removed:',\n      });\n    }\n  }\n\n  // ── pdg-mode flip forces full writeback (#2099 F1) ─────────────────\n  // The incremental writeback persists only changed-file nodes, so a pdg\n  // config differing from the one the DB rows were built under cannot be\n  // reconciled incrementally: off→on silently drops the freshly built CFG\n  // layer (\"Incremental: changed=0\", zero BasicBlock rows), on→off strands\n  // zombie blocks for unchanged files. MUST sit before the alreadyUpToDate\n  // fast path below — a clean-tree flip would otherwise early-return without\n  // running the pipeline at all. The notice is deliberately NOT gated on\n  // options.force: --skills implies force with no message of its own, and a\n  // mode change deserves a diagnostic regardless of why a rebuild happens.\n  if (existingMeta && pdgModeMismatch(existingMeta.pdg, options)) {","sourceCodeStart":1380,"sourceCodeEnd":1416,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/run-analyze.ts#L1380-L1416","documentation":"Thrown as an `LbugWipeError` during dirty-state recovery when `quarantineSidecarsForDirtyRecovery` reports `failed.length > 0` — the interrupted run's WAL/shadow/lock sidecars could neither be moved aside nor removed. Failing fast here (in seconds) with the same typed error the eventual DB-wipe would throw is strictly better than running the entire pipeline only to die at the wipe step, which would waste minutes and zero embeddings. The message is self-contained because the HTTP server forwards only `err.message` over worker IPC.","triggerScenarios":"`existingMeta.incrementalInProgress` is truthy (dirty flag from a crashed run), `quarantineSidecarsForDirtyRecovery(lbugPath, log)` returns a non-empty `failed` array. This means file-system operations (rename or unlink) on the `.wal`/`.shadow`/`.lock` sidecars failed after all retries.","commonSituations":"Another process (a lingering MCP server, IDE extension, or a second `gitnexus serve`) holds a handle on the sidecar files; Windows antivirus or delete-pending handle-release lag; a read-only or permission-restricted `.gitnexus` directory; NFS or network filesystem locking issues.","solutions":["Close any other process that may have the database open: stop `gitnexus serve`, close IDE extensions, and kill stale `gitnexus` processes.","Re-run `gitnexus analyze` immediately — the handle may have been released by now (the error message itself says 'an immediate re-run often succeeds').","On Windows, wait a few seconds for antivirus/delete-pending handles to release, or exclude `.gitnexus/` from AV scanning.","If the `.gitnexus` directory is on a network filesystem, move it to local storage or fix the mount's locking behavior.","As a last resort, manually delete the sidecar files listed in the error (`.wal`, `.shadow`) and re-run."],"exampleFix":"// before\ngitnexus analyze\n// LbugWipeError: Cannot start dirty-state recovery...could neither be moved aside nor removed:\n//   - .gitnexus/store.lbug.wal\n//   - .gitnexus/store.lbug.shadow\n// after\n# kill any process holding the handle\ngitnexus analyze   # immediate re-run often succeeds","handlingStrategy":"retry","validationCode":"// Before analyze, check for processes that might hold DB handles:\n// (On Linux/Mac: lsof +D .gitnexus; on Windows: handle.exe or tasklist)\n// Programmatically, check if the lock file is held:\nimport { existsSync } from 'fs';\nif (existsSync(path.join(repoPath, '.gitnexus', 'store.lbug.lock'))) {\n  console.warn('A lock file exists. Ensure no other gitnexus process is running.');\n}","typeGuard":"import { isLbugWipeError } from './lbug/lbug-adapter.js';\n// LbugWipeError is exported; check by name:\nconst isWipeError = (err: unknown): boolean =>\n  err instanceof Error && err.name === 'LbugWipeError';","tryCatchPattern":"// Retry pattern — the error message itself says 'an immediate re-run often succeeds':\nfor (let attempt = 1; attempt <= 3; attempt++) {\n  try {\n    await runAnalyze(options);\n    break;\n  } catch (err) {\n    if (err instanceof Error && err.name === 'LbugWipeError' && attempt < 3) {\n      console.warn(`Wipe failed (attempt ${attempt}), retrying...`);\n      await new Promise((r) => setTimeout(r, 2000));\n      continue;\n    }\n    throw err;\n  }\n}","preventionTips":["Never run two `gitnexus analyze` or `gitnexus serve` processes against the same `.gitnexus` directory simultaneously.","On Windows, add `.gitnexus/` to antivirus exclusions to prevent delete-pending handle lag.","Ensure `.gitnexus/` is on a local filesystem, not NFS/network mounts with locking issues."],"tags":["crash-recovery","filesystem","file-lock","ladybugdb","windows","analyze"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}