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.
ssh: Could not resolve hostname github.com: Name or service not known
How to fix 'ssh: Could not resolve hostname github.com: Name or service not known' in Git
error: insufficient permission for adding an object to repository database .git/objects
How to fix "insufficient permission for adding an object to repository database" in Git
fatal: could not create work tree dir 'repo': Permission denied
How to fix "could not create work tree dir: Permission denied" in Git
Smudge error: Error downloading object: The requested URL returned error
How to fix Git LFS 'Smudge error: Error downloading object' error
fetch-pack: unexpected disconnect while reading sideband packet
How to fix 'unexpected disconnect while reading sideband packet' in Git