santifer/career-ops · critical

Validation failed ( ) and revert also failed ( )

Error message

Validation failed (${err.message}) and revert also failed (${revertErr.message})

What it means

After applying updates, update-system.mjs validates the result (e.g. via git status). If validation fails it tries to revert the touched paths with revertPaths(); if that revert also fails, it throws this combined error chaining the original validation failure via `cause` so neither diagnostic is lost. It signals the worktree is in an unknown, partially-reverted state needing manual attention.

Solutions

  1. Read err.cause (`cause`) to see the original validation error and fix that root problem.
  2. Remove a stale lock file if present: check for .git/index.lock and delete it if no git process is running.
  3. Manually revert the updated files: `git checkout -- <paths>` or `git restore <paths>`.
  4. Re-run `node update-system.mjs apply --confirm` after the repo is healthy.

Example fix

# before (broken state)
rm -f .git/index.lock
git restore modes/ scripts/  # or: git checkout -- .
# after
node update-system.mjs apply --confirm
Defensive patterns

Strategy: try-catch

Validate before calling

// before applying, ensure the repo is healthy:
execFileSync('git', ['status', '--porcelain'], { cwd: ROOT });
execFileSync('git', ['diff', '--quiet']);

Try / catch

try {
  applyUpdate();
} catch (e) {
  if (e.message.startsWith('Validation failed') && e.cause) {
    console.error('root cause:', e.cause.message);
    console.error('revert manually: git restore .');
  } else throw e;
}

Prevention

When it happens

Trigger: Post-apply validation threw (err), revertPaths(updated, initialStatusPaths) also threw (revertErr) — typically because whatever broke `git status` (index lock, corrupt index) also breaks `git checkout --`.

Common situations: A stale .git/index.lock left by another git process; concurrent git operations during the update; disk-full or permission problems in the repo; an editor or watcher holding files during apply.

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16). Data as JSON: /api/errors/3bc4152e4c451063. Report an issue: GitHub.

Appendix: source

Thrown at update-system.mjs:2427

      const changed = gitStatusEntries()
        .map((entry) => entry.path)
        .filter((file) => !initialStatusPaths.has(file) && !generatedBackupPaths.has(file));
      for (const file of userLayerViolations(changed, updatePaths, effectiveUserPaths())) {
        console.error(`SAFETY VIOLATION: User file was modified: ${file}`);
        violatedUserPaths.add(file);
      }
    } catch (err) {
      // Fail closed: if we can't validate the safety invariant we must
      // not silently proceed — that would let a real violation slip
      // through. Revert what we already applied and abort.
      console.error(`Aborting: could not validate user-layer safety (${err.message}).`);
      try {
        revertPaths(updated, initialStatusPaths);
      } catch (revertErr) {
        // If the revert itself fails (likely whatever broke `git
        // status` also broke `git checkout --`), don't lose the
        // original validation error — chain it via `cause`.
        throw new Error(
          `Validation failed (${err.message}) and revert also failed (${revertErr.message})`,
          { cause: err },
        );
      }
      throw err;
    }

    if (violatedUserPaths.size > 0) {
      console.error('Aborting: user files were touched. Rolling back system files...');
      // Revert ONLY the system-layer updates — never `git checkout` the
      // violated user paths back to HEAD. Doing so would overwrite the
      // user's working-tree content (accumulated STAR+R stories, local
      // edits) with whatever is committed upstream, causing data loss.
      // The user files were flagged as touched by the update, not by the
      // user; leaving them as-is is the safe choice — the user decides
      // what to do with them.
      const violation = new Error('Update aborted: user files were touched.');
      try {

View on GitHub (pinned to e7abd431fc)