ANTIGRAVITY LABJP
Articles/Antigravity Basics
Antigravity Basics/2026-05-21Intermediate

Why Antigravity Agents Hit `Permission denied` on git push, and How to Fix It for Good

Your Antigravity agent finishes the edits, tests pass, the commit message is sharp — and then git push fails with `remote: Permission to ... denied` or `fatal: Authentication failed`. Here's the diagnostic flow I actually use for multi-account, multi-PAT setups, plus the long-term fixes that keep the error from coming back.

antigravity429git12github5permission-deniedtroubleshooting105

You hand an agent in Antigravity a meaningful refactor, it works through the change cleanly, tests come back green, the commit message even reads like something you'd have written yourself — and then the final git push lights up red with remote: Permission to masakihirokawa/xxx.git denied to other-account. If you've spent any real time with Antigravity, you've probably felt that small deflating moment. I run four Dolice Labs sites in parallel — each with its own Personal Access Token — alongside the iOS and Android apps I've been shipping as an indie developer since 2014 (the AdMob revenue line on those apps now sits at 50M+ cumulative downloads). For Hirokawa, this category of failure has tripped me up more times than I'd like to admit.

The good news is that the request almost always reached GitHub's servers — it's just being rejected because the credentials the agent handed over don't match the repository. The less good news is that agent-driven workflows have more ways to get those credentials tangled than a human typing things by hand. What follows is the diagnostic order I actually run through, plus the structural fixes I rely on to stop this from recurring.

Read the error message first — it tells you where you're stuck

Before anything else, sort the red text into one of three categories. Each one points to a completely different layer of the problem.

  • remote: Permission to {owner}/{repo}.git denied to {user}: the request reached GitHub, the account on the other end was identified, but that account does not have write access to this repo
  • fatal: Authentication failed for 'https://github.com/...': credentials are missing or invalid — usually an expired PAT, a missing scope, or a typo
  • fatal: unable to access ... Could not resolve host / Connection timed out: this never made it out of your network — suspect a proxy or VPN, not Git

The first one is by far the most common in Antigravity workflows, so the rest of this article focuses there.

Find out who the agent is trying to push as

Agents are focused on the task you gave them; they have no opinion about which credential they're using right now. Your first job is to make the implicit choice visible.

# 1. Confirm the remote URL — HTTPS or SSH, and which owner
git remote -v
 
# 2. Confirm credential.helper is in play
git config --get credential.helper
git config --list --show-origin | grep -i credential
 
# 3. (macOS) See which GitHub credential the keychain actually serves
security find-internet-password -s github.com -g 2>&1 | grep "acct\|svce"

Between these three commands you can usually narrate the situation in one sentence: "This repo is on HTTPS for masakihirokawa/xxx.git, but the keychain is handing back a PAT registered under acct=other-account." The moment the keychain's acct= and the remote's owner disagree, you've found the cause.

In multi-account setups like mine — same host, different identities — osxkeychain tends to act as if "github.com" should only have one credential. In my own logs, seven or eight out of ten Permission denied failures came down to a stale PAT camping out in the keychain.

Three workable patterns for keeping accounts separate

Once you know the cause, the next question is structural: how do you guarantee the agent uses the right credential every time, not just once? In practice I rely on three patterns. Each has its own trade-offs.

1. Embed the PAT directly in the remote URL (when you want full per-project isolation)

If you want a specific repo, in a specific clone, to always push under one specific PAT, the cleanest move is to put the credentials in the remote URL itself.

# Bind the masakihirokawa PAT to this clone, full stop
git remote set-url origin https://masakihirokawa:ghp_xxxxxxxxxxxx@github.com/masakihirokawa/example.git
 
# Verify
git remote -v

The win here is independence: it doesn't matter what your credential helper or keychain thinks — this clone will push under that PAT. Any terminal the agent opens in this repo will behave identically.

The cost is that the PAT lives in .git/config in plaintext. Don't do this in a shared clone, and don't include such configs in backups. I reserve this approach for short-lived clones under /tmp or strictly-local working copies that I would never share.

2. Per-URL credential routing (when you want to keep using the keychain but split accounts)

If you'd rather keep osxkeychain in the picture, you can ask Git to use different usernames for different URL prefixes. That's enough to coax the keychain into storing — and returning — a separate entry per account.

# Anything under github.com/masakihirokawa is "masakihirokawa"
git config --global credential.https://github.com/masakihirokawa.username masakihirokawa
 
# Anything under github.com/other-account is "other-account"
git config --global credential.https://github.com/other-account.username other-account

After setting this up, push once under each account so each gets its own keychain entry. From that point on Git will look at the remote's owner and pull the matching credential automatically.

This pattern plays nicely with workflows where the agent shallow-clones into a fresh path (in my case /tmp/repos/{site}) every run — the routing is keyed to URL ownership, not to any per-clone setting.

3. SSH with per-account keys via ~/.ssh/config (when you want the most durable answer)

If PAT expiry has worn you down, SSH is the calmest long-term home. Generate a key per account and route them via host aliases:

# ~/.ssh/config
Host github-masakihirokawa
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_masakihirokawa
  IdentitiesOnly yes
 
Host github-other-account
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_other_account
  IdentitiesOnly yes

Then point each clone at the alias: git@github-masakihirokawa:masakihirokawa/example.git. No more PAT rotations creeping up on you at the worst moment.

The catch is that you can't really teach an agent to mentally rewrite git@github.com:... into git@github-masakihirokawa:... at clone time. I keep SSH for stable automation paths (my publishing pipeline runs on SSH) and use pattern 1 or 2 everywhere else.

How to keep agents from re-stepping into the same trap

Once the mechanics are sorted, the remaining work is operational — making sure an agent that knows nothing about your setup still does the right thing on the first try.

The single highest-leverage thing is documenting the contract in AGENTS.md (or .claude/AGENTS.md). One line like "This repo expects its remote to be set as https://masakihirokawa:${ENV_PAT}@github.com/... — do not run git remote set-url" will stop the agent from helpfully reconfiguring things into a broken state.

Next, keep the PAT itself out of code. I store mine in a separate file inside the workspace and pull them on demand with grep -A1 "Site Name" tokens.txt | tail -1. The agent reads the token at runtime; it never sits in a .env it might commit by reflex.

Finally, git config --global push.default current is a small but useful belt-and-braces setting. When the agent gets lazy and just types git push, it pushes the current branch — not whatever the upstream might think it wants. It removes a whole adjacent class of "wait, that didn't go where I expected" failures.

When Permission denied still won't go away

If you've worked through the patterns above and the error is still there, run this final three-item checklist:

  • PAT scope and expiry: in GitHub → Settings → Developer settings → Personal access tokens, confirm the token is still valid and that it has the repo scope
  • Org SSO authorization: for org-owned repos, generating the PAT isn't enough — you have to go to Configure SSO and explicitly authorize it for that org
  • Duplicate keychain entries: open Keychain Access, search for github.com, and delete stale PAT entries by hand. The keychain can hold multiple entries for the same host; a security find-internet-password -s github.com before and after will show you what changed

I haven't yet run into a case where all three are clean and Permission denied survives. Whatever you find, write it down — a one-line memory file pays for itself the second time the same repo gives you the same error six months later.

Antigravity's agents are remarkable, but the authentication layer is one of those places where they still need humans to lay the groundwork. Put the structure in place once, and the rest of the year you get to focus on the actual work instead of re-typing PATs. I hope this saves someone else an evening.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Antigravity2026-07-02
Parallel Agents Multiply Artifacts Too — Designing Lifespans and Cleanup for Intermediate Outputs
Worktrees, screenshots, temp branches — parallel agents leave debris at parallel speed. A design for defining artifact lifespans and automating cleanup without ever destroying uncommitted work.
Antigravity2026-05-31
Why Your Antigravity Agent Stops Mid-Task with 429 RESOURCE_EXHAUSTED, and How to Fix It
When you hand a long task to an Antigravity agent, it sometimes halts halfway with a red 429 RESOURCE_EXHAUSTED. That is a rate-limit or quota signal, not a bug. Here is how I diagnose the three flavors of 429 in production, and how to keep your agent from stalling on the same wall twice.
Antigravity2026-05-28
Fixing Antigravity Google Sign-in That Won't Return From the Browser
When you press Sign in with Google in Antigravity and the browser just hangs without returning to the editor, the cause is rarely a single thing. Here is how I narrow it down across four layers — callback port, third-party cookies, stale session files, and network path — using only commands I keep close on every machine.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →