jarrodwatts/claude-hud · critical · Error

Unable to resolve an absolute git.exe from PATH

Error message

Unable to resolve an absolute git.exe from PATH

What it means

On Windows, WindowsGitRunner spawns a detached worker process with shell:false and an absolute path to git.exe, so it never relies on PATH lookup at spawn time. During construction it calls resolveWindowsGitExecutable(), which scans every absolute entry of the PATH environment variable for a git.exe that exists and is a regular file (src/git-runner.ts:289-312). If no such entry is found, the constructor throws 'Unable to resolve an absolute git.exe from PATH' — the library refuses to run because the worker could not launch git.

Source

Thrown at src/git-runner.ts:92

    return { stdout };
  }

  async close(): Promise<void> {
    // Direct commands have no session process to release.
  }
}

class WindowsGitRunner implements GitCommandRunner {
  private readonly worker: ChildProcess;
  private nextId = 1;
  private pending: PendingCommand | null = null;
  private exited = false;
  private closing = false;

  constructor(private readonly cwd: string) {
    const workerPath = fileURLToPath(new URL('./windows-git-worker.js', import.meta.url));
    const gitExecutable = resolveWindowsGitExecutable();
    if (!gitExecutable) throw new Error('Unable to resolve an absolute git.exe from PATH');

    this.worker = spawn(process.execPath, [workerPath, gitExecutable], {
      env: createGitEnvironment(),
      windowsHide: true,
      shell: false,
      stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
    });

    this.worker.on('message', (message: unknown) => this.handleMessage(message));
    this.worker.once('error', (error) => this.failPending(error));
    this.worker.once('exit', (code, signal) => {
      this.exited = true;
      if (!this.closing) {
        this.failPending(new Error(`Git worker exited unexpectedly (${signal ?? code ?? 'unknown'})`));
      }
    });
  }

View on GitHub (pinned to 939eb66485)

Solutions

  1. Install Git for Windows (or point PATH at an existing install's cmd directory, e.g. C:\Program Files\Git\cmd) and restart the shell/process so PATH is picked up.
  2. Verify with 'where git.exe' in cmd (or 'Get-Command git.exe' in PowerShell) that git.exe resolves via an absolute PATH entry; fix PATH ordering/entries if not.
  3. If PATH is intentionally sanitized, set a PATH environment variable containing the absolute directory of git.exe before constructing the runner.
  4. Use a machine where git.exe exists on disk at an absolute path; relative PATH directories are deliberately ignored by the resolver for security (repo-controlled lookups).
  5. As a last resort, patch/extend the environment passed to the runner so PATH includes the Git cmd directory (createGitEnvironment preserves PATH-based resolution).

Example fix

// before (no git.exe reachable from PATH)
const runner = new WindowsGitRunner(repoCwd); // throws

// after (ensure PATH includes Git before constructing)
process.env.PATH = ['C:\\Program Files\\Git\\cmd', process.env.PATH].join(path.win32.delimiter);
const runner = new WindowsGitRunner(repoCwd);
Defensive patterns

Strategy: try-catch

Validate before calling

import { resolveWindowsGitExecutable } from './src/git-runner.js';

function canConstructWindowsGitRunner(): boolean {
  return resolveWindowsGitExecutable() !== null;
}

if (!canConstructWindowsGitRunner()) {
  // fix PATH / install Git before proceeding
}

Type guard

function hasGitExecutable(
  resolveCandidate: (c: string) => string | null = (c) => {
    try { const r = require('fs').realpathSync(c); return require('fs').statSync(r).isFile() ? r : null; } catch { return null; }
  },
): boolean {
  return resolveWindowsGitExecutable(process.env, resolveCandidate) !== null;
}

Try / catch

let runner: WindowsGitRunner;
try {
  runner = new WindowsGitRunner(cwd);
} catch (err) {
  if (err instanceof Error && err.message.includes('Unable to resolve an absolute git.exe')) {
    // fall back: report missing Git, or use a different runner / disable git features
    throw new Error('Git for Windows not found on PATH; install it or fix PATH.', { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: Constructing WindowsGitRunner (new WindowsGitRunner(cwd)) on Windows when resolveWindowsGitExecutable returns null: (1) PATH env var is unset or empty; (2) PATH contains only relative entries (entries that are not path.win32.isAbsolute are skipped); (3) no PATH entry contains git.exe, or the git.exe candidate fails statSync/isFile (e.g. Git not installed, or only git.cmd/git-wrapper present); (4) PATH entries contain embedded NUL characters or fail realpathSync.

Common situations: Git for Windows not installed or installed in a non-PATH location (e.g. portable Git); running under a service/CI account whose PATH lacks the usual 'C:\Program Files\Git\cmd'; PATH stripped or overridden by a sanitized environment (spawn env customization, containers, GUI-launched apps inheriting minimal PATH); Git exposed only as git.exe via a shell alias or via git.cmd in cmd/ with an older layout where git.exe sits elsewhere; corrupted PATH with quoted or malformed entries.


AI-assisted analysis of jarrodwatts/claude-hud@939eb66485 (2026-08-29). Data as JSON: /api/errors/074305a389595c3e. Report an issue: GitHub.