hasura/graphql-engine · critical

rename old to exe: inconsistent state, re-install cli: %w

Error message

rename old to exe: inconsistent state, re-install cli: %w

What it means

The worst-case self-update failure: the current binary was renamed to '<exe>.old', then renaming the new binary into place failed, and the rollback rename (old -> exe) ALSO failed (cli/update/update.go:180). The CLI is left with no executable at its expected path; the error explicitly tells the user to re-install because the updater cannot recover automatically.

Source

Thrown at cli/update/update.go:180

	err = os.Rename(exe, oldExe)
	if err != nil {
		return errors.E(op, fmt.Errorf("rename exe to old: %w", err))
	}

	// rename the new binary as the current binary
	err = os.Rename(newExe, exe)
	if err != nil {
		// rename unsuccessful
		//
		// The filesystem is now in a bad state. We have successfully
		// moved the existing binary to a new location, but we couldn't move the new
		// binary to take its place. That means there is no file where the current executable binary
		// used to be!
		// Try to rollback by restoring the old binary to its original path.
		rerr := os.Rename(oldExe, exe)
		if rerr != nil {
			// rolling back failed, ask user to re-install cli
			return errors.E(op, fmt.Errorf(
				"rename old to exe: inconsistent state, re-install cli: %w",
				rerr))
		}
		// rolled back, throw update error
		return errors.E(op, fmt.Errorf("rename new to exe: %w", err))
	}

	// rename success, remove the old binary
	errRemove := os.Remove(oldExe)

	// windows has trouble removing old binaries, so hide it instead
	// it will be removed next time this code runs.
	if errRemove != nil {
		_ = hideFile(oldExe)
	}

	return nil
}

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Re-install the CLI using your original install method (install script, package manager, go install) — recovery by hand is only for experts.
  2. As a last resort, manually restore: mv '<exe>.old' back to the exe path if the .old file still exists.
  3. On Windows, exclude the CLI install directory from real-time antivirus scanning before updating again.
  4. Ensure stable permissions and disk space before retrying updates.

Example fix

# recovery when .old survived
$ ls /usr/local/bin/cli*  # see cli.old / .cli.new
$ mv /usr/local/bin/cli.old /usr/local/bin/cli
$ chmod +x /usr/local/bin/cli
# then re-install properly
Defensive patterns

Strategy: fallback

Validate before calling

if _, err := os.Stat(exe); err != nil {
    if _, oerr := os.Stat(exe + ".old"); oerr == nil {
        // recovery possible: .old exists
    }
}

Try / catch

if err := update.ApplyUpdate(v); err != nil {
    if strings.Contains(err.Error(), "inconsistent state, re-install cli") {
        os.Rename(exe+".old", exe) // best-effort manual recovery
        return fmt.Errorf("update broke install; restored from .old, please re-install: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: ApplyUpdate where the second os.Rename(newExe, exe) fails (e.g. the .new file disappeared or was quarantined by antivirus) and the subsequent os.Rename(oldExe, exe) also fails (permissions changed, disk error, locked files).

Common situations: Antivirus quarantining the freshly downloaded .new binary mid-swap; disk filling or permissions changing between the two renames; power loss or forced process kill during the swap window leaving an inconsistent state.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/4d3553ba67303945. Report an issue: GitHub.