Every few months somebody discovers, again, that Git authentication is a junk drawer full of legacy decisions, partial integrations, and optimistic documentation. The latest version of this ritual goes like this. You run gh auth login. GitHub CLI smiles. It tells you authentication is complete. You are logged in as the correct user. Then you run the most boring command in software engineering:

git clone https://github.com/ORG/REPO.git

And GitHub replies with:

remote: Write access to repository not granted.
fatal: unable to access 'https://github.com/ORG/REPO.git/': The requested URL returned error: 403

At this point normal people start questioning their permissions, their operating system, and the moral legitimacy of HTTPS itself.

The annoying truth is simpler. gh being authenticated does not guarantee that plain old git over HTTPS is using the same credential. Those are related systems, not one system. On Linux in particular, they often drift apart in funny and infuriating ways.

The symptom that fools people

The most confusing version of this failure is when all of the following are true at once:

That feels impossible. But it is not impossible. It just means the browser, gh, and git are each authenticating differently. The browser is using your web session. gh is using its stored token, or thinks it is. git is using whatever credential path it happens to find first, which may be no credential at all, an invalid token, a stale token, or an environment variable somebody forgot about three weeks ago.

The first culprit: GH_TOKEN in the environment

The fastest way to sabotage yourself is to have GH_TOKEN exported in your shell startup files, environment manager, desktop launcher, or some automation wrapper. GitHub CLI will happily notice it. In fact it will often prioritize it over interactive login. If that token is invalid, under-scoped, or just plain placeholder garbage like YOUR_TOKEN, you get a wonderful hybrid state where gh sort of looks configured while real operations fail.

Check for that first:

env | grep -E 'GIT|GH|SSH'

# and

gh auth status

If you see something like:

GH_TOKEN=YOUR_TOKEN
X Failed to log in to github.com using token (GH_TOKEN)
- The token in GH_TOKEN is invalid.

then congratulations, you have found the gremlin. Remove it from the current shell:

unset GH_TOKEN

Then log in again cleanly:

gh auth login

If you restart your terminal and the problem comes back, the variable is being re-exported somewhere, usually ~/.bashrc, ~/.profile, a shell plugin, or a launcher environment.

The second culprit: gh auth and git auth are not the same thing

Even after you successfully log in with gh, plain git clone https://... may still fail. This is the part that feels like a personal insult, because it is exactly the job users think GitHub CLI was invented to do.

Conceptually, here is what is happening:

There is a command that sounds like it should bridge this gap:

gh auth setup-git

Sometimes it does. Sometimes it does not. Sometimes it quietly configures integration that is still defeated by some other environment problem. If you run it and cloning still fails, do not spend the afternoon arguing with reality. Move to a more direct test.

The definitive test

If this command works:

git clone "https://YOUR_USERNAME:$(gh auth token)@github.com/ORG/REPO.git"

then three important things are proven immediately:

  1. Your GitHub account really does have access to the repo.
  2. Your current gh token is valid for that repo.
  3. The failure is in how git is sourcing credentials, not in GitHub permissions.

This is ugly, but it is a wonderful diagnostic. It turns a vague “GitHub is broken” feeling into a precise statement: the HTTPS transport works if you hand it the token directly.

Why the error message is so bad

GitHub loves returning:

remote: Write access to repository not granted.

during operations that are not trying to write anything. You are cloning. You want read access. Yet the error sounds like you tried to force-push to production while wearing oven mitts. This is one of those messages that is technically adjacent to the problem while being emotionally disastrous for troubleshooting.

When you see it during clone, read it as: “the authenticated identity being presented to GitHub is wrong, missing, invalid, or insufficient for this private repository.”

What to do in order, without going insane

Here is the sequence I recommend because it narrows the problem fast instead of making you randomly jab commands into the terminal.

  1. Confirm the repo is visible in the browser while logged into the expected GitHub account.
  2. Run gh auth status.
  3. If GH_TOKEN is present, run unset GH_TOKEN.
  4. Run gh auth login again.
  5. Try gh repo view ORG/REPO to confirm the GitHub CLI session can see it.
  6. Run gh auth setup-git.
  7. Try the normal HTTPS clone again.
  8. If it still fails, try the explicit token URL clone.

If step 8 works, stop doubting yourself. The account and token are fine. The local Git credential plumbing is the problem.

Permanent fixes versus tactical fixes

The explicit token URL is a tactical fix. It gets you unblocked. It is not the prettiest forever solution.

A cleaner long-term setup is one of these:

SSH is often less psychologically damaging once it is set up properly, but of course it comes with its own rite of passage: host key verification, missing keys, wrong keys, keychain weirdness, and the classic Permission denied (publickey).

So yes, HTTPS auth is messy. SSH auth is also messy. The real lesson is that Git has accumulated multiple credential mechanisms over time, and GitHub CLI only papers over part of that complexity.

What I would tell every Linux user

If you are doing private GitHub work on Linux, assume there are four layers that can each fail independently:

  1. Browser session access
  2. GitHub CLI auth state
  3. Git credential helper or HTTPS transport auth
  4. SSH agent and SSH key configuration

Do not treat “I can see the repo on github.com” as proof that git clone will work. It proves almost nothing except that your browser cookies are fine.

Do not treat “gh says I am logged in” as proof that plain git is using the same identity. It often is not.

And if an explicit $(gh auth token) URL clone works while normal clone fails, you have permission to be annoyed. That is not user error. That is tooling fragmentation dressed up as a seamless developer experience.

The short version

When gh auth login succeeds but git clone still returns 403 on a private repo:

If that last form works, the repo permissions are fine. Your Git credential path is the thing that needs fixing.

Which is a very Git sentence, unfortunately.

Request a quote or call 0432 000 583 to discuss your website, app, database, or custom software project.

E-business card (QR ready) for conferences and in-person shares. · Site map

Copyright © 2026 Industrial Hypertext - Software Development Perth, Western Australia | All rights reserved