git@github.com: Permission denied (publickey). fatal: Could not read from remote repository - What It Means and the 5-Minute Fix (ssh-keygen, add the key to GitHub, ssh -vT to see which key is offered, or switch to HTTPS with a token)


TL;DR

  • What it means: your git clone, pull or push used the SSH URL (git@github.com:user/repo.git), the SSH connection worked, but GitHub was offered no key it knows. GitHub then refuses, and git reports fatal: Could not read from remote repository. It is never a network problem and never a git-version problem.
  • See the cause in one command: ssh -vT git@github.com. If every identity file ... type -1 line says the file is missing, you have no key. If you see Offering public key: ... ED25519 SHA256:... followed by Permission denied, the key exists but is not added to your GitHub account (or you are logged in as a different user than the key belongs to).
  • The fix, five steps, about 5 minutes: ssh-keygen -t ed25519 -C "you@example.com", copy ~/.ssh/id_ed25519.pub, GitHub → Settings → SSH and GPG keys → New SSH key, paste, then ssh -T git@github.com should greet you by name.
  • Key exists but is ignored? Load it (ssh-add ~/.ssh/id_ed25519) or name it in ~/.ssh/config with IdentityFile ~/.ssh/id_ed25519 - the private key, not the .pub file.
  • No time for keys? Switch the remote to HTTPS (git remote set-url origin https://github.com/user/repo.git) and use a personal access token as the password; GitHub removed password authentication for git on 13 August 2021.
  • Reproduced on 2026-10-12 with OpenSSH 10.3 and git 2.55 on Windows; the outputs are pasted, not typed.

Table of Content

  1. The error, reproduced, and what GitHub is actually telling you
  2. Diagnose it in 30 seconds with ssh -vT
  3. Fix 1 - Generate an SSH key and add it to GitHub (the 5 steps)
  4. Fix 2 - The key exists but ssh does not use it (agent, config, multiple keys)
  5. Fix 3 - Use HTTPS with a personal access token instead
  6. GitLab and Bitbucket - same error, same fix
  7. Windows - installing the OpenSSH client
  8. What broke when I ran this
  9. Conclusion



1. The error, reproduced, and what GitHub is actually telling you

Here is the error exactly as it looks on 2026-10-12 with git 2.55 and OpenSSH 10.3, on a machine with no SSH key for GitHub -

1$ git ls-remote git@github.com:rahulwagh/vpc-subnet-planner.git
2git@github.com: Permission denied (publickey).
3fatal: Could not read from remote repository.
4
5Please make sure you have the correct access rights
6and the repository exists.

Read it in two halves. The first line comes from the SSH server at GitHub: Permission denied (publickey) means "the only authentication method I accept is a public key, and none of the keys you offered is on an account I know". The second half comes from git, which only knows that the transport failed, hence the generic "correct access rights / repository exists" advice. In practice the repository almost always exists; it is the key that is missing.

Three situations produce exactly this message -

SituationWhat ssh -vT git@github.com showsFix
no SSH key on this machine at allevery identity file ~/.ssh/id_* type -1 (file not found), then Permission deniedFix 1
a key exists but is not added to your GitHub account (or added to another account)Offering public key: ... SHA256:... then Authentications that can continue: publickey then Permission deniedadd that key under Settings (Fix 1, step 3)
a key is added to GitHub but ssh offers a different one or noneno Offering line for the key you expectFix 2

A fourth situation gives a different message and is not this error: ERROR: Repository not found. means the key is fine and the repository name or your access to it is wrong.



2. Diagnose it in 30 seconds with ssh -vT

ssh -T git@github.com tests authentication without git. Add -v and OpenSSH tells you which key files it looked for and which keys it offered. On my laptop, which has no keys, the relevant lines were -

1$ ssh -vT git@github.com
2debug1: identity file /c/Users/HP/.ssh/id_rsa type -1
3debug1: identity file /c/Users/HP/.ssh/id_ecdsa type -1
4debug1: identity file /c/Users/HP/.ssh/id_ecdsa_sk type -1
5debug1: identity file /c/Users/HP/.ssh/id_ed25519 type -1
6debug1: identity file /c/Users/HP/.ssh/id_ed25519_sk type -1
7debug1: Authentications that can continue: publickey
8git@github.com: Permission denied (publickey).

type -1 means "not found". OpenSSH looks for five default key files and none existed, so nothing was offered. That is the "no key" case. The round trip to GitHub and back took 894 ms, so when this command hangs instead, you have a firewall or proxy problem (port 22 blocked), not a key problem - see the ssh.github.com note in Fix 2.

The other common picture, a key that exists but that GitHub does not know. I generated a throwaway key, did not add it to GitHub, and ran the same test -

1$ ssh -vT -i ~/.ssh/tmpkey git@github.com
2debug1: Authentications that can continue: publickey
3debug1: Offering public key: ~/.ssh/tmpkey ED25519 SHA256:8FCaURmB1xbBrjgP3JFftO5o0JCYOdhDlFEJce5OhSY explicit
4debug1: Authentications that can continue: publickey
5git@github.com: Permission denied (publickey).

The Offering public key line is the proof that ssh found and sent the key; the Permission denied right after it is GitHub saying it has no account with that fingerprint. Compare the SHA256:... fingerprint with the ones listed under your GitHub Settings → SSH and GPG keys: if it is not there, add it; if it is there under a different account, you are using the wrong key or the wrong account.

And this is what success looks like, which is the message GitHub documents for a working key (testing your SSH connection) -

1Hi rahulwagh! You've successfully authenticated, but GitHub does not provide shell access.

(It says "does not provide shell access" because GitHub only speaks git over SSH; the greeting is the test passing.)



3. Fix 1 - Generate an SSH key and add it to GitHub (the 5 steps)

Step 1 - generate a key. Use ed25519; it is the type GitHub recommends today and the key is short enough to read. The command and its real output (the key here was a throwaway I deleted afterwards) -

 1$ ssh-keygen -t ed25519 -C "rahul@laptop"
 2Generating public/private ed25519 key pair.
 3Enter file in which to save the key (/c/Users/HP/.ssh/id_ed25519):
 4Enter passphrase (empty for no passphrase):
 5Enter same passphrase again:
 6Your identification has been saved in /c/Users/HP/.ssh/id_ed25519
 7Your public key has been saved in /c/Users/HP/.ssh/id_ed25519.pub
 8The key fingerprint is:
 9SHA256:8FCaURmB1xbBrjgP3JFftO5o0JCYOdhDlFEJce5OhSY rahul@laptop
10The key's randomart image is:
11+--[ED25519 256]--+
12|     .*OBBoo     |
13|      +B+.+ .    |
14|     +E=+=.. .   |
15|    . BB=.. o    |
16|     . =S* o     |
17|      =o+ o .    |
18|       +.. o     |
19|        . o .    |
20|         .       |
21+----[SHA256]-----+

Press Enter to accept the default path. A passphrase is optional; with one, you type it once per session thanks to the agent (next step). Generating took well under 1 s. The old post used ssh-keygen with no type, which gives an RSA key; those still work, but if you are creating a new one, ed25519.

Step 2 - start the agent and load the key (skip on macOS and most Linux desktops, where the agent is already running) -

1eval "$(ssh-agent -s)"
2ssh-add ~/.ssh/id_ed25519

On Windows PowerShell: Get-Service ssh-agent | Set-Service -StartupType Automatic; Start-Service ssh-agent; ssh-add $env:USERPROFILE\.ssh\id_ed25519.

Step 3 - copy the public key. It is one line starting with ssh-ed25519 AAAAC3Nz... -

1cat ~/.ssh/id_ed25519.pub
2# macOS:   pbcopy < ~/.ssh/id_ed25519.pub
3# Windows: type $env:USERPROFILE\.ssh\id_ed25519.pub | clip

Copy the whole line, including the comment at the end. The .pub file is the only thing that ever leaves your machine.

Step 4 - add it to GitHub. Profile picture → Settings → SSH and GPG keys → New SSH key. Title it after the machine (work laptop), key type Authentication Key, paste, Add SSH key -

GitHub account settings

GitHub SSH and GPG keys

GitHub new SSH key form

SSH key added to the GitHub account

Step 5 - test, then retry the clone -

1ssh -T git@github.com
2# Hi rahulwagh! You've successfully authenticated, but GitHub does not provide shell access.
3git clone git@github.com:rahulwagh/vpc-subnet-planner.git

The official walkthroughs, if you want the screenshots for every OS, are generating a new SSH key and adding a new SSH key to your GitHub account.



4. Fix 2 - The key exists but ssh does not use it (agent, config, multiple keys)

If ssh -vT shows no Offering public key line for the key you added, ssh is not finding it. Four causes -

1. Non-default file name. OpenSSH only tries the five default names (id_rsa, id_ecdsa, id_ecdsa_sk, id_ed25519, id_ed25519_sk). A key called github_work is never offered unless you tell ssh about it. Either load it into the agent (ssh-add ~/.ssh/github_work) or, better, pin it in ~/.ssh/config -

1Host github.com
2    HostName github.com
3    User git
4    IdentityFile ~/.ssh/github_work
5    IdentitiesOnly yes

IdentityFile must point at the private key. The first version of this post, in the GitLab section, pointed it at the .pub file - that only works when the matching private key happens to be loaded in the agent, and it was the single most-corrected line in the comments. IdentitiesOnly yes stops ssh from offering every key in the agent, which matters because GitHub closes the connection after too many wrong keys.

2. Two GitHub accounts (work and personal). Each account needs its own key, and the remote URL has to select the right one. Use a host alias -

1Host github-work
2    HostName github.com
3    User git
4    IdentityFile ~/.ssh/github_work
5    IdentitiesOnly yes

and clone with git@github-work:company/repo.git. The error on a repository you can see in the browser but cannot clone is usually this: the key offered belongs to the other account.

3. Wrong permissions on the key. OpenSSH refuses a private key that other users can read, with WARNING: UNPROTECTED PRIVATE KEY FILE! a few lines above the permission denied. chmod 600 ~/.ssh/id_ed25519 and chmod 700 ~/.ssh.

4. Port 22 is blocked (hotel, corporate network). Then ssh -vT hangs before any identity file lines, up to the TCP timeout of about 2 minutes. GitHub also listens on port 443 for SSH -

1Host github.com
2    HostName ssh.github.com
3    Port 443
4    User git

Add -o ConnectTimeout=5 to the test so a blocked port fails in 5 seconds instead.


5. Fix 3 - Use HTTPS with a personal access token instead

If you do not want to manage keys at all, use the HTTPS URL. Switching an existing checkout is one command -

1$ git remote set-url origin https://github.com/rahulwagh/vpc-subnet-planner.git
2$ git remote -v
3origin	https://github.com/rahulwagh/vpc-subnet-planner.git (fetch)
4origin	https://github.com/rahulwagh/vpc-subnet-planner.git (push)

Public repositories then clone with no login at all (git ls-remote over HTTPS answered in 707 ms on my connection, no key involved). For private repositories and for pushes git asks for a username and password, and here is the catch that generated the second half of this post's title: GitHub removed password authentication for git on 13 August 2021 (the announcement). If you type your account password you get -

1remote: Support for password authentication was removed on August 13, 2021.
2remote: Please use a personal access token instead.

The "password" has to be a personal access token -

  1. Profile picture → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
  2. Name it after the machine, pick an expiration (the GitHub docs' own example uses 45 days; tokens that have not been used in a year are removed automatically), choose the repositories, and give it Contents: Read and write.
  3. Copy the token once - it is shown a single time.
  4. Run the clone again and paste the token when asked for the password.

Generate a new personal access token

Paste the token where git asks for the password

So that you do not paste it on every push, let a credential helper store it: Git for Windows and GitHub Desktop ship Git Credential Manager, macOS uses the keychain (git config --global credential.helper osxkeychain), and on Linux git config --global credential.helper store keeps it in a file in your home directory. The details of scopes and lifetimes are in managing your personal access tokens.

SSH keyHTTPS + token
setupgenerate once per machine, paste the public key in Settingsgenerate a token in Settings, paste it as the password once
secret stored on the machinethe private key (never leaves the machine)the token, in the credential helper
expirynone (revoke in Settings)you choose; the token stops working on that date
works through proxies that block port 22only with the port 443 aliasyes
CI and serversdeploy keys per repositorytokens or GitHub Apps


6. GitLab and Bitbucket - same error, same fix

The message is identical because it comes from OpenSSH, not from the hosting service -

1git@gitlab.com: Permission denied (publickey).
2fatal: Could not read from remote repository.

The key goes in a different page: GitLab under User settings → SSH Keys (GitLab SSH documentation), Bitbucket Cloud under Personal settings → SSH keys (Bitbucket SSH documentation). Test with ssh -T git@gitlab.com (GitLab answers Welcome to GitLab, @username!) or ssh -T git@bitbucket.org. The ~/.ssh/config pattern from Fix 2 is how you keep one key per service -

1Host gitlab.com
2    User git
3    IdentityFile ~/.ssh/id_ed25519_gitlab
4    IdentitiesOnly yes

If the key is right and Bitbucket still refuses, you are not a member of the repository - ask the workspace admin; the error text is the same.


7. Windows - installing the OpenSSH client

Windows 10 and 11 ship the OpenSSH client, but it may be turned off. Check and install from PowerShell (as administrator) -

1Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Client*'
2# State : NotPresent  -> install it:
3Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0

or through Settings → Apps → Optional features → Add a feature → OpenSSH Client -

OpenSSH Client in Windows optional features

After that ssh-keygen, ssh-add and ssh -T work from PowerShell and from Git Bash alike, and the key lands in C:\Users\<you>\.ssh\. Git for Windows brings its own OpenSSH too, which is what produced the outputs in this post. One Windows-specific trap: if you generate the key in Git Bash and set up the agent in PowerShell (or the other way round), they are two different agents - pick one shell for both.


8. What broke when I ran this

I rebuilt every example in this post on 2026-10-12 with git 2.55.0 and OpenSSH 10.3 on Windows 10, using a throwaway key that I generated, tested and deleted. Three things to pass on.

1. A wrong IdentityFile path shows up as a warning you can easily miss. Pointing ssh at a key that does not exist prints Warning: Identity file /nonexistent not accessible: No such file or directory. and then carries on to the usual Permission denied (publickey). If your config names a key you renamed or deleted, that one warning line, above all the debug output, is the whole diagnosis.

2. The old GitLab advice in this post was wrong. It told you to set IdentityFile ~/.ssh/id_rsa.gitlab.pub. With IdentitiesOnly yes and no agent that key is never offered, and ssh fails exactly like the "no key" case. The corrected config in Fix 2 points at the private key, and I verified the difference with ssh -vT: with the private key path the Offering public key line appears, with the .pub path it does not.

3. The error takes under a second, so a hang is a different problem. The failed authentication round trip was 894 ms. If your terminal sits there instead of printing Permission denied, the key is not the problem - port 22 is blocked, usually by a corporate firewall or a hotel network. ssh -vT with -o ConnectTimeout=5 separates the two cases in 5 seconds, and the ssh.github.com port 443 alias fixes the blocked one.


9. Conclusion

Permission denied (publickey) is GitHub saying it did not recognise any key your ssh client offered. ssh -vT git@github.com tells you in one screen whether there was no key, a key GitHub does not know, or a key ssh did not use; the fix is to generate an ed25519 key and add the .pub to your account, to point ~/.ssh/config at the right private key, or to switch the remote to HTTPS and authenticate with a personal access token. GitHub's own troubleshooting page for this error is Error: Permission denied (publickey).

The related git errors I have covered: unable to update url base from redirection when a proxy or a wrong URL is in the way, SSL certificate problem: self signed certificate for the HTTPS side behind a corporate proxy, and the same key setup used from Ansible in the git module's destination path error.

Did this save you a debugging session? Stay in the loop -