abhigyanpatwari/GitNexus · error · Error

Analyzer identity input changed while it was being hashed: $

Error message

Analyzer identity input changed while it was being hashed: ${candidate}

What it means

hashStableFile() streams a file through a 256 KiB buffer, hashing as it goes, and validates that the stat snapshot taken before the open, the fstat of the opened descriptor, the byte count actually read, and a fresh stat after reading all agree; after two failed attempts it declares the input unstable. The receipt hash must reflect exact bytes, so a file mutated while being hashed (changed size, mtime, or inode) is rejected rather than hashed incorrectly.

Source

Thrown at gitnexus/src/core/analyzer-identity.ts:585

        bytes += read;
        if (bytes > expectedBytes) break;
      }
      const openedAfter = statState(fstatSync(descriptor, { bigint: true }));
      closeSync(descriptor);
      descriptor = null;
      const after = snapshotReadableFile(candidate);
      if (
        bytes === expectedBytes &&
        isDeepStrictEqual(openedBefore, openedAfter) &&
        isDeepStrictEqual(before, after)
      ) {
        return { digest: `sha256:${hash.digest('hex')}`, state: after, bytes };
      }
    } finally {
      if (descriptor !== null) closeSync(descriptor);
    }
  }
  throw new Error(`Analyzer identity input changed while it was being hashed: ${candidate}`);
}

function resolveExistingPath(candidate: string): string {
  return realpathSync.native(path.resolve(candidate));
}

/**
 * Case-stabilize a path's Windows drive letter so two processes that observed
 * the same directory under different drive-letter casing (`c:\…` vs `C:\…`)
 * produce byte-identical analyzer-identity path fields (#2668).
 *
 * `realpathSync.native` canonicalizes 8.3 short names and symlinks but does not
 * guarantee the drive-letter case it returns — it can preserve whatever casing
 * the caller's path carried, and `import.meta.url` casing depends on how each
 * entry process (CLI shim vs `npx`/npm wrapper vs server worker) was launched.
 * When `analyze` stamps `build.rootPath` under one casing and `status`
 * recomputes it under another, `analyzerRunnerIdentitiesEqual` deep-compares
 * unequal and `status` reports a freshly-analyzed, untouched repo as stale.

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Re-run analyze after builds, installs, and sync tools are quiescent.
  2. Sequence CI steps so 'gitnexus analyze' runs strictly after the install/build step, not in parallel.
  3. Exclude the gitnexus install and dist directories from cloud-sync folders and real-time antivirus scanning.
  4. For programmatic use, retry the resolve a bounded number of times (same pattern as error 40).

Example fix

// before
const identity = resolveAnalyzerRunnerIdentity(import.meta.url);

// after
async function resolveWithRetry(url: string, tries = 3) {
  for (let i = 0; ; i += 1) {
    try {
      return resolveAnalyzerRunnerIdentity(url);
    } catch (error) {
      const message = String((error as Error).message);
      if (i === tries - 1 || !/changed while (it was being|its identity was being)/.test(message)) throw error;
      await new Promise((resolve) => setTimeout(resolve, 250 * (i + 1)));
    }
  }
}
Defensive patterns

Strategy: retry

Type guard

function isIdentityHashChangedError(error: unknown): boolean {
  return error instanceof Error && /^Analyzer identity input changed while it was being hashed:/.test(error.message);
}

Try / catch

for (let attempt = 0; attempt < 3; attempt += 1) {
  try {
    identity = resolveAnalyzerRunnerIdentity(import.meta.url);
    break;
  } catch (error) {
    if (!isIdentityHashChangedError(error) || attempt === 2) throw error;
    await sleep(250 * (attempt + 1));
  }
}

Prevention

When it happens

Trigger: Reached for every build-tree file and hashed runtime artifact during resolveAnalyzerRunnerIdentity — e.g. 'gitnexus analyze' hashing dist/ while a bundler/watcher (tsc --watch, esbuild, vite) rewrites a .js/.wasm/.node file, or an install/upgrade process replaces files under node_modules or dist mid-scan.

Common situations: Running gitnexus in one terminal while 'npm run build'/'npm install' runs in another; Docker volume mounts with lazy materialization mutating mtimes; synced folders (Dropbox/OneDrive) downloading files during the scan; antivirus rewriting files after inspection on Windows.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/0e6da692145431ff. Report an issue: GitHub.