All guidesMore providers: databases, auth, hosting, email and payments
On this page

GitHub error

GitHub

remote: error: GH013: Repository rule violations found for refs/heads/main

By Ohad Krispin, founder of VibeRaven · LinkedIn

"remote: error: GH013: Repository rule violations found for refs/heads/main" with "Push cannot contain secrets" under it means GitHub push protection scanned the commits you pushed, matched a supported secret pattern in at least one of them, and rejected the push. Deleting the value in a new commit does not clear it: GitHub says to remove the secret from all of the commits it appears in. If the latest commit added it, remove it and run git commit --amend --all, then git push. If an earlier commit did, run git rebase -i on the parent of the earliest commit that has it. A bypass link is in the output, but a bypass can create an alert.

What does the full GH013 output look like?

The lines below combine the output posted in a GitHub Community thread and a DEV post with the example in GitHub's command line guide. The secret type changes with the key found, and the branch name is the one you pushed. The part to read is the list of commits and paths under the secret type, and the URL on the "To push" line.

remote: error: GH013: Repository rule violations found for refs/heads/main.
remote: - GITHUB PUSH PROTECTION
remote:   Resolve the following violations before pushing again
remote: - Push cannot contain secrets
remote:   <secret type, for example GitHub Personal Access Token>
remote:    locations:
remote:      - commit: 8728dbe67
remote:        path: README.md:4
remote: (?) To push, remove secret from commit(s) or follow this URL to allow the secret.
remote: https://github.com/<owner>/<repo>/security/secret-scanning/unblock-secret/<id>
error: failed to push some refs to 'github.com:<owner>/<repo>.git'

Why does deleting the key in a new commit not fix the push?

Push protection scans every commit in the push, not only the final files. GitHub's guide says: "To resolve a blocked push, you must remove the secret from all of the commits it appears in." A coding agent that wrote a key into a file, or committed .env, leaves the value in that commit even if a later commit deletes the file.

If the value sat in an env file, the git commands on the committed .env page show which commits touched it. These are the steps from GitHub's guide.

# the latest commit added the secret
# remove it from the code, then:
git commit --amend --all
git push

# an earlier commit added the secret
git log
git rebase -i <COMMIT-ID>~1
# in the editor, change pick to edit on that commit, save and close
# remove the secret from the code, then:
git add .
git commit --amend
git rebase --continue
git push

Only for a value that is not a live credential. GitHub's guide says to open the URL from the blocked push as the same user who pushed, choose a reason (It's used in tests, It's a false positive, or I'll fix it later), click "Allow me to push this secret", and retry the push within three hours. A bypass adds an alert, and "I'll fix it later" creates an open one. If there is no bypass option, you must remove the secret or request bypass privileges.

A key you bypass is pushed. If it is a real key, rotate it with its provider; see what to do after a secret scanning alert.

Checklist

How to fix it, then check the fix

  1. 01Read the output: note the secret type and every commit and path under locations.
  2. 02
    Find the earliest commit that has the value, using a path from the output:
    git log --oneline --name-status -- <path from the output>
  3. 03Remove the value from every commit that has it: git commit --amend --all when it is the latest commit, an interactive rebase from its parent when it is earlier.
  4. 04Read the key from an environment variable such as process.env.SUPABASE_SERVICE_ROLE_KEY, set it in Vercel, add .env* to .gitignore and keep only placeholders in .env.example.
  5. 05If the key was ever pushed, pasted into a public place or bypassed, create a new one with its provider and revoke the old one, then update Vercel and redeploy.
  6. 06Run git push again. The push succeeds without the GH013 lines when no commit in it still matches a secret pattern.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Why am I seeing this on a private repository?

GitHub says push protection for your user account is on by default and stops you pushing secrets to public repositories, while repository-level push protection is off by default until an administrator, owner or security manager turns it on. A block on a private repository therefore points to a setting someone enabled for that repository or its organization.

Which keys does push protection block?

Only supported patterns, and only their most recent token versions: GitHub says push protection supports the most recent token versions that secret scanning can identify with confidence. In its pattern table, push protection covers Supabase secret keys (supabase_secret_key), Stripe API keys, OpenAI API keys and Resend API keys, among others. It lists no Supabase service_role or anon JWT, so a push that passes does not show the repo has no keys. See the next page for that gap.

Does anyone else see that I bypassed it?

GitHub says a bypass of repository-level push protection creates an alert in the Security and quality tab, adds the event to the audit log and emails owners, security managers and repository administrators watching the repository. A bypass of user-level push protection alone creates no alert.