windmill-labs/windmill · error

git ${args.join(" ")} failed (exit ${status}): ${r.stderr ??

Error message

git ${args.join(" ")} failed (exit ${status}): ${r.stderr ?? ""}

What it means

The internal git() helper in cli/src/utils/git.ts runs arbitrary `git <args>` via spawnSync for git-sync deploy operations (checkout of the deploy branch, staging, commit, push). When the command exits non-zero and allowFail is not set, it throws this error with the full arg list, exit code, and git's stderr. It is a generic wrapper: the stderr text identifies the real underlying git failure.

Source

Thrown at cli/src/utils/git.ts:525

    if (has((t) => t === "settings")) forcedIncludes.includeSettings = true;
    if (has((t) => t === "key")) forcedIncludes.includeKey = true;
  }

  return { extraIncludes, forcedIncludes };
}

function git(
  args: string[],
  opts?: { allowFail?: boolean },
): { status: number; stdout: string; stderr: string } {
  const r = spawnSync("git", args, { encoding: "utf8", stdio: "pipe" });
  const status = r.status ?? 1;
  if (r.error) {
    if (opts?.allowFail) return { status, stdout: "", stderr: String(r.error) };
    throw r.error;
  }
  if (status !== 0 && !opts?.allowFail) {
    throw new Error(
      `git ${args.join(" ")} failed (exit ${status}): ${r.stderr ?? ""}`,
    );
  }
  return { status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
}

// Checkout (or create) the dedicated deploy branch, mirroring the hub script:
// try `git checkout <branch>`, on failure create it with -b and enable
// push.autoSetupRemote so the subsequent bare `git push` targets it.
export function checkoutGitSyncDeployBranch(branch: string): void {
  const existing = git(["checkout", branch], { allowFail: true });
  if (existing.status === 0) {
    log.info(`Switched to existing branch ${branch}`);
    return;
  }
  git(["checkout", "-b", branch]);
  git(["config", "--add", "--bool", "push.autoSetupRemote", "true"]);
  log.info(`Created and switched to branch ${branch}`);

View on GitHub (pinned to e474e8803c)

Solutions

  1. Read the stderr suffix in the message — it names the actual git failure — and address it directly.
  2. For push rejections: `git pull --rebase` the deploy branch (gitSyncDeployPush already retries with rebase), or resolve conflicts manually and push.
  3. On CI, configure git identity: `git config --global user.name/user.email` before deploying.
  4. Fix remote authentication (SSH key/agent, or refreshed credential helper token).
  5. Ensure the expected files/paths exist locally before the deploy, and that HEAD is not in a state that blocks checkout (commit or stash).

Example fix

// before: CI container without git identity
git commit ... failed (exit 128): fatal: unable to auto-detect email address
// after
git config --global user.name "deploy-bot"
git config --global user.email "deploy-bot@example.com"
wmill sync push --git-sync ...
Defensive patterns

Strategy: try-catch

Validate before calling

import { spawnSync } from "node:child_process";
// Pre-flight checks before a git-sync deploy
function preflightGit(): void {
  const inRepo = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], { encoding: "utf8" });
  if (inRepo.status !== 0) throw new Error("not inside a git work tree");
  for (const [k, v] of [["user.name", null], ["user.email", null]] as const) {
    const c = spawnSync("git", ["config", k], { encoding: "utf8" });
    if (c.status !== 0) throw new Error(`git ${k} is not configured`);
  }
  const remote = spawnSync("git", ["ls-remote", "--exit-code", "origin", "HEAD"], { encoding: "utf8" });
  if (remote.status !== 0) throw new Error(`cannot reach remote 'origin': ${remote.stderr}`);
}

Try / catch

try {
  checkoutGitSyncDeployBranch(branch);
  gitSyncDeployPush(params);
} catch (e) {
  if (String(e).includes("failed (exit")) {
    const stderr = String(e).split("): ")[1] ?? "";
    if (stderr.includes("rejected")) {
      // non-fast-forward: pull --rebase the deploy branch, then retry
      spawnSync("git", ["pull", "--rebase", "origin", branch]);
    } else if (stderr.includes("unable to auto-detect email")) {
      spawnSync("git", ["config", "--global", "user.email", "bot@example.com"]);
    }
  }
  throw e;
}

Prevention

When it happens

Trigger: Any non-zero git exit during git-sync deploy: `git checkout <branch>` failing for the -b creation path, `git add` on a missing path, `git commit` with no user identity configured, `git push` rejected (non-fast-forward, auth failure, missing upstream), or r.error being absent but status non-zero.

Common situations: git-sync deploy where the remote branch moved (push rejected, needs rebase); missing git user.name/user.email on CI containers; bad or expired credentials for the remote; target path not present locally so `git add` fails; detached HEAD or conflicting local changes blocking checkout; rerunning when the deploy branch already exists is NOT an error (checkout uses allowFail).

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 windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/2479d9392982cb38. Report an issue: GitHub.