Every git pull hangs for 30 seconds and then fails with remote: HTTP Basic: Access denied or fatal: Authentication failed for 'https://github.com/...'. Or Git opens an empty credential prompt in a terminal that cannot accept interactive input — common in VS Code terminals, SSH sessions, and CI runners. Alternatively, on macOS your Keychain shows a red exclamation mark next to the GitHub credential, meaning the stored token has been revoked and every operation that tries to use it fails immediately. Credential helpers bridge Git and your system’s secret store; when that bridge breaks, nothing involving a remote works until it is repaired.
Common causes
Ordered by hit rate, highest first.
1. Personal access token (PAT) was revoked or expired
GitHub, GitLab, and Bitbucket allow setting PAT expiry dates. Once the token expires, every operation returns 401/403. The credential helper faithfully supplies the stale token, which is rejected.
How to spot it: Log in to the web UI and go to Settings > Developer Settings > Personal Access Tokens. If the token shows “Expired” or “Revoked,” that is the cause.
2. macOS Keychain has a cached bad credential
After a password change or token rotation, macOS Keychain still supplies the old credential. Git receives a 401, re-prompts, and if the terminal cannot show a prompt, it hangs or fails.
How to spot it: Open Keychain Access, search for “github.com” — you will see an entry with the old password. Or run git credential reject interactively to purge it.
3. credential.helper is set to a binary that does not exist
After a macOS upgrade or Homebrew update, git-credential-osxkeychain or git-credential-manager may have moved. Git falls back to re-prompting on every operation.
How to spot it: git config --global credential.helper returns a path. Run which <helper-name> or the full path directly — if it returns “not found,” the helper is missing.
4. SSH key was not added to the SSH agent
You switched from HTTPS to SSH remote URLs (git@github.com:...), but the SSH key is not loaded in ssh-agent. Every SSH operation prompts for the passphrase in a context where interactive input is unavailable.
How to spot it: ssh -T git@github.com returns “Permission denied (publickey)” or hangs. ssh-add -l returns “The agent has no identities.”
5. Corporate SSO requires re-authorization of the PAT
GitHub Enterprise or GitHub with SAML SSO requires that each PAT be explicitly authorized for the organization. A new token that was not SSO-authorized fails with 403: Resource protected by organization SAML enforcement.
How to spot it: The error message mentions “SAML” or “SSO.” The PAT settings page on GitHub shows the token needs “Enable SSO” clicked next to the organization name.
6. Git Credential Manager is in a deadlock on Windows
Git Credential Manager (GCM) on Windows sometimes opens a UI dialog behind the terminal window or in a different desktop session, causing the terminal to block indefinitely waiting for the dialog.
How to spot it: The git push hangs with no output for more than 30 seconds. Switching to the desktop or checking the taskbar reveals a hidden authentication dialog.
Shortest path to fix
Step 1: Erase the stale cached credential
# macOS — osxkeychain helper
git credential-osxkeychain erase <<EOF
host=github.com
protocol=https
EOF
# Linux — libsecret / gnome-keyring
git credential-gnome-keyring erase <<EOF
host=github.com
protocol=https
EOF
# Cross-platform — Git Credential Manager
git credential reject <<EOF
host=github.com
protocol=https
EOF
Step 2: Generate a new PAT and store it
Go to GitHub Settings > Developer Settings > Personal Access Tokens > Fine-grained tokens. Create a token with repo scope. Then:
# On macOS, the keychain helper will prompt once and cache the new token
git fetch origin # enter username + new PAT when prompted
Step 3: Fix a missing credential helper binary
git config --global credential.helper # shows current value
# If it returns a path that doesn't exist:
brew install git-credential-manager # macOS/Linux
# or
winget install --id Git.GCM # Windows
git config --global credential.helper manager # re-register GCM
Step 4: Fix SSH key not loaded in agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh -T git@github.com # should return "Hi username!"
For permanent persistence across sessions, add to ~/.ssh/config:
Host github.com
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
Step 5: Authorize PAT for SSO
- Go to GitHub Settings > Developer Settings > Personal Access Tokens.
- Click the token name.
- Under “Organization access,” click “Enable SSO” next to each organization.
- Complete the SSO authorization flow in the browser.
Step 6: Test the fix
git fetch --dry-run origin
git push --dry-run origin main
Both should complete without prompting or hanging.
Prevention
- Use SSH keys with
ed25519instead of HTTPS PATs for interactive developer machines — SSH keys do not expire by default and do not require credential helper configuration. - Set PAT expiry reminders in your calendar 7 days before the token expires so you rotate proactively.
- In CI, use environment-variable-based authentication (
GIT_ASKPASSorGITHUB_TOKEN) rather than credential helpers, which are designed for interactive use. - Store the credential helper binary path relative to a known location (e.g., managed by Homebrew), and pin the helper version in a team Brewfile.
- Enable
credential.useHttpPath = trueif you have credentials for multiple organizations on the same host to prevent cross-organization credential confusion. - For GitHub Apps or service accounts, use GitHub Apps tokens (short-lived, auto-rotated) rather than long-lived PATs.
- Document the credential setup steps in your onboarding guide with exact commands for each OS.
FAQ
Q: Can I avoid credential prompts entirely in a CI/CD pipeline?
A: Yes. Use git config --global url."https://x-access-token:${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" to rewrite all GitHub URLs to include the token inline. Never use this on developer machines.
Q: How do I use different credentials for different GitHub organizations?
A: Set credential.useHttpPath = true and the credential helper will store separate credentials per repository URL. Alternatively, use SSH with different keys per organization configured in ~/.ssh/config using Host aliases.
Q: git credential approve and reject — what do these do?
A: git credential approve stores a credential (username + password/token) in the configured helper. git credential reject erases the stored credential for the given host/protocol combination. Both read a credential specification from stdin.
Q: After rotation, old builds in CI still fail. Why? A: CI runners often cache secrets at the job or pipeline level. After rotating a PAT, update the secret in every CI secret store (GitHub Actions secrets, GitLab CI/CD variables, Jenkins credentials) and re-run any failed pipelines.