{"record":{"id":"3bc4152e4c451063","repo":"santifer/career-ops","slug":"validation-failed-err-message-and-revert-also","errorCode":null,"errorMessage":"Validation failed (${err.message}) and revert also failed (${revertErr.message})","messagePattern":"Validation failed \\((.+?)\\) and revert also failed \\((.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"update-system.mjs","lineNumber":1290,"sourceCode":"        for (const userPath of USER_PATHS) {\n          if (file.startsWith(userPath)) {\n            console.error(`SAFETY VIOLATION: User file was modified: ${file}`);\n            violatedUserPaths.add(file);\n          }\n        }\n      }\n    } catch (err) {\n      // Fail closed: if we can't validate the safety invariant we must\n      // not silently proceed — that would let a real violation slip\n      // through. Revert what we already applied and abort.\n      console.error(`Aborting: could not validate user-layer safety (${err.message}).`);\n      try {\n        revertPaths(updated, initialStatusPaths);\n      } catch (revertErr) {\n        // If the revert itself fails (likely whatever broke `git\n        // status` also broke `git checkout --`), don't lose the\n        // original validation error — chain it via `cause`.\n        throw new Error(\n          `Validation failed (${err.message}) and revert also failed (${revertErr.message})`,\n          { cause: err },\n        );\n      }\n      throw err;\n    }\n\n    if (violatedUserPaths.size > 0) {\n      console.error('Aborting: user files were touched. Rolling back system files...');\n      // Revert ONLY the system-layer updates — never `git checkout` the\n      // violated user paths back to HEAD. Doing so would overwrite the\n      // user's working-tree content (accumulated STAR+R stories, local\n      // edits) with whatever is committed upstream, causing data loss.\n      // The user files were flagged as touched by the update, not by the\n      // user; leaving them as-is is the safe choice — the user decides\n      // what to do with them.\n      const violation = new Error('Update aborted: user files were touched.');\n      try {","sourceCodeStart":1272,"sourceCodeEnd":1308,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/update-system.mjs#L1272-L1308","documentation":"Thrown only when TWO independent failures stack inside apply()'s post-update safety validation. First, the loop that iterates gitStatusEntries() to confirm no user-layer files were touched throws (so the safety invariant cannot be evaluated — the code fails closed rather than assume safety). Second, the immediate remedy revertPaths(updated, initialStatusPaths) — which runs `git checkout --` over the system paths just applied — also throws. The original validation error is preserved via `cause` so neither diagnostic is lost; the comment notes whatever broke `git status` usually also breaks `git checkout --`.","triggerScenarios":"apply() has already checked out system paths from the upstream ref when git enters a broken state mid-update: a corrupt .git/index, a stale .git/index.lock left by a killed prior process, a permissions/ownership change on .git, a full disk, or the git binary disappearing from PATH. gitStatusEntries() then throws, the revert via git checkout throws for the same root cause, and this combined error is raised.","commonSituations":"Disk filled mid-update (writes succeed for checkout, then index read fails); a previous update-system.mjs was SIGKILLed and left .git/index.lock behind; running as a different user than owns .git (permissions); .git/index corruption after a crash or fsync failure; antivirus/FS monitor locking .git on Windows; git upgraded/downgraded across a format-version boundary.","solutions":["Inspect err.cause first — it holds the original validation failure (usually the git error); that root cause is what both steps share.","Remove a stale lock if present: `rm -f .git/index.lock` (only if no git process is running), then re-run `node update-system.mjs apply`.","Repair a corrupt index: `git read-tree HEAD` or, if that fails, `rm .git/index && git reset` (mixed reset to HEAD, preserving working tree) to rebuild it, then retry the updater.","Free disk space if full, then retry — checkout writes can leave the tree half-applied; run `git status` + `git checkout -- .` to reconcile before re-running apply.","Verify git is on PATH and not mid-upgrade (`git --version`); a broken git install makes both status and checkout fail identically.","If .git ownership changed, run `git config --global --add safe.directory <ROOT>` to clear the 'dubious ownership' refusal."],"exampleFix":"# before: apply aborts with the combined error, repo left mid-update\nnode update-system.mjs apply\n# after: repair the shared root cause (stale lock), then re-run\nrm -f .git/index.lock\ngit status   # confirm git works again\nnode update-system.mjs apply","handlingStrategy":"try-catch","validationCode":"// Before applying, sanity-check git is operational on ROOT — the same two\n// operations that fail (status + checkout) are what the validator and revert need.\nimport { execFileSync } from 'node:child_process';\nfunction gitHealthy(root) {\n  const probes = [\n    ['status', '--porcelain'],\n    ['checkout', '-q', '--', '.'], // no-op on a clean tree; reveals lock/corruption\n  ];\n  for (const args of probes) {\n    try { execFileSync('git', args, { cwd: root, stdio: 'ignore', timeout: 30000 }); }\n    catch (e) { return { ok: false, args, error: e.message }; }\n  }\n  return { ok: true };\n}\n// gate the update: const h = gitHealthy(ROOT); if (!h.ok) -> repair before apply.","typeGuard":null,"tryCatchPattern":"// Distinguish the double-failure from a plain update error so repair logic runs.\ntry {\n  await import('./update-system.mjs'); // or spawn apply\n} catch (err) {\n  const msg = String(err?.message || err);\n  if (msg.startsWith('Validation failed (') && msg.includes('and revert also failed')) {\n    // err.cause is the original git error; repair git state, then re-run.\n    console.error('Repo in broken git state:', err.cause?.message || err.cause);\n    console.error('Repair path: rm -f .git/index.lock; git read-tree HEAD');\n    process.exit(2);\n  }\n  throw err;\n}","preventionTips":["Never SIGKILL the updater — let it reach its `finally` so it releases .update-lock cleanly (a leaked lock is the most common precursor).","Keep free disk space; checkout + index writes fail mid-way when full.","Don't switch the user account owning .git between runs (dubious-ownership refusal breaks both git status and checkout).","Pre-flight: `git status` works before scheduling an automated update."],"tags":["git","update-system","corruption","filesystem","atomicity","lock"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}