This error occurs when using git push --force-with-lease and your local tracking branch has outdated information about the remote. The fix usually involves running git fetch --prune to sync your local refs with the actual remote state.
The "rejected (stale info)" error is a safety mechanism built into Git's `--force-with-lease` option. When you use `git push --force-with-lease`, Git compares your local knowledge of the remote branch with its actual current state. If they don't match, Git refuses the push to prevent you from accidentally overwriting commits that you didn't know about. This error specifically means that your local repository's cached information about the remote branch (stored in `refs/remotes/origin/`) is out of date or "stale." The most common cause is that the remote branch was deleted and recreated, or someone else pushed changes that your local repo doesn't know about. The "stale info" variant is distinct from the standard "non-fast-forward" rejection. It indicates a mismatch between what your local Git thinks the remote looks like versus its actual state, rather than simply having divergent commit histories. This protection is especially important in collaborative workflows where force-pushing is sometimes necessary (like after rebasing), but you want to ensure you're not accidentally erasing a teammate's work.
The most common fix is to run git fetch with the --prune flag, which removes local references to remote branches that no longer exist:
git fetch --pruneThis updates your local knowledge of the remote and removes any stale tracking branches. After this, your --force-with-lease should work if your local branch was actually up to date.
Before force-pushing, inspect what commits exist on the remote that you might not have locally:
# See the difference between your local and remote
git log origin/main..HEAD
git log HEAD..origin/mainIf the second command shows commits, someone pushed changes you don't have. You should integrate those changes before force-pushing.
After fetching with prune, try your push again:
git push --force-with-lease origin mainIf the remote truly hasn't changed since your fetch, this should now succeed.
To prevent this issue in the future, configure Git to automatically prune stale refs on every fetch:
git config --global fetch.prune trueThis ensures your local remote-tracking branches always stay in sync with the actual remote state.
If you've confirmed that no one else has pushed changes and you still can't push, you can use regular --force:
git push --force origin mainWarning: This bypasses the safety check. Only use this when you're certain you won't overwrite anyone else's work. In team environments, coordinate with your team before force-pushing.
### Understanding --force-with-lease
The --force-with-lease flag was added to Git as a safer alternative to --force. It works by checking if your local remote-tracking ref matches the actual remote ref before allowing the push. Think of it as "force push, but only if the remote hasn't changed since I last looked."
The "lease" terminology comes from distributed systems: you're asking for a lease on the remote ref based on your last known state.
### The Fetch-Then-Push Caveat
Be aware that running git fetch immediately before git push --force-with-lease essentially makes it equivalent to git push --force. The safety mechanism relies on your potentially-outdated knowledge of the remote. If you always fetch first, you're always "up to date" from Git's perspective, even if you never actually looked at what was fetched.
### CI/CD Considerations
In CI/CD environments, this error commonly occurs when:
1. Cached Git repositories contain stale refs from previous runs
2. Shallow clones don't have complete ref information
3. Multiple pipeline runs race to push to the same branch
Solutions for CI:
# Always prune before operations
git fetch --prune --unshallow || git fetch --prune
# Or use a fresh clone
git clone --depth 1 <repo>### Specifying the Expected Ref
For more precise control, you can specify exactly what you expect the remote to look like:
git push --force-with-lease=main:origin/main origin mainThis explicitly states your expectation and will fail if the remote doesn't match, regardless of your local tracking branch state.
### Remote Prune Command
You can also manually prune without fetching:
git remote prune originThis removes stale remote-tracking branches without downloading new objects.
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server
fatal: unable to access: Proxy auto-configuration failed
How to fix 'Proxy auto-configuration failed' in Git
fatal: unable to access: Authentication failed (proxy requires basic auth)
How to fix 'Authentication failed (proxy requires basic auth)' in Git
fatal: unable to access: no_proxy configuration not working
How to fix 'no_proxy configuration not working' in Git
fatal: unable to read tree object in treeless clone
How to fix 'unable to read tree object in treeless clone' in Git