I'd just pushed a blog post. Five minutes later I pushed the next one and GitHub said no:
$ git push origin master
remote: Internal Server Error
remote: Request ID FB6B:1C94E9:200692:2B209D:6AC67A11
remote: Time 2026-10-07T16:57:56Z
To github.com:dusandz/ddz.dev.git
! [remote rejected] master -> master (Internal Server Error)
error: failed to push some refs to 'github.com:dusandz/ddz.dev.git' No hook name, no policy, no hint. Just a server error with a request ID, from a push that was two commits and a 140 KB JPEG. The push before it, same repo, same laptop, had gone through at 16:52.
My first thought was the JPEG. My second was the commit message, which has a Unicode ellipsis in it. Both wrong, and I'd have saved myself ten minutes by reading the output properly before touching anything.
"unpack ok" means the failure came later
A push is two steps on the server. First it unpacks the objects you sent. Then it runs its checks and moves the branch to point at the new commit. The rejection line only tells you the second step didn't happen. To see how the first went, ask git to show the protocol:
GIT_TRACE_PACKET=1 git push origin master 2>&1 | grep -E 'unpack|ng |Internal Server Error' packet: sideband< \1000eunpack ok
packet: sideband< \2Internal Server Error
packet: sideband< \1002fng refs/heads/master Internal Server Error unpack ok is the server saying every object you sent unpacked without complaint. (They sit in a quarantine directory until the push is accepted, so "stored" is too strong; "received and readable" is right.) ng is the per-ref status, and the text after the ref name is the server's reason. Here it's the same three words, which is no reason at all. Whatever broke, it broke after unpacking and before refs/heads/master moved.
That doesn't clear the JPEG or the commit message on its own, since GitHub's own checks run after unpacking too. But it does say a corrupt or oversized pack isn't the story, and it narrows the question to one: is it something about these commits, or is it the repo?
One push that proves it isn't you
Rather than reason about it, test it. Push something GitHub cannot possibly object to: a commit it already has, to a branch name that doesn't exist yet.
git push origin origin/master:refs/heads/push-test ! [remote rejected] origin/master -> push-test (Internal Server Error) No new objects go over the wire, just an empty pack and a request to create a branch pointing at a commit the server has had for minutes. Hooks still run on a push like that, so it isn't quite nothing. But there's no new content for anything to object to, and no protection rule on a branch that doesn't exist. If this fails the same way, your new commits aren't the cause.
For completeness I also tried the obvious splits before I thought of the no-op push: the same two commits to a new branch, a commit with only the Markdown, a commit with only the image. All three failed the same way. The no-op push would have told me that in one go.
Meanwhile everything that isn't a push worked:
git ls-remote origin master # answered, with the old SHA
gh api repos/dusandz/ddz.dev --jq .pushed_at # 2026-10-07T16:52:35Z And githubstatus.com showed every component green, Git Operations included. The status page is for incidents big enough to be declared. Whatever this was, it wasn't.
The fix is a loop
There's nothing to fix locally, so the only move is to retry on a timer and go do something else:
for i in $(seq 1 10); do
git push origin master && break
echo "attempt $i failed at $(date -u +%H:%M:%S)"
sleep 60
done attempt 1 failed at 16:59:24
attempt 2 failed at 17:00:26 The third attempt, at 17:01:29, went through. From the first failure to the successful push was about four minutes. The next push after that, with another image, worked first time. Same commits, same laptop, same remote, nothing changed on my side. That's as close to proof of "it was them" as you get without a post-mortem.
It's also not rare. The only page on Google's first page that contains this exact error text is an Ask HN thread titled "Is GitHub down for you as well?", where a dozen people from Helsinki to New York paste the same [remote rejected] ... (Internal Server Error) line. The poster edited in "one minute after posting this it's working again", and one commenter got through on attempt four. Same shape, same fix, and the status page said "some users" at best.
What this error is not
The same [remote rejected] shape carries different messages, and the words in the parentheses are the whole diagnosis:
(pre-receive hook declined),(protected branch hook declined)orpush declined due to repository rule violationsis GitHub saying no on purpose: branch protection, push protection finding a secret, a ruleset. The lines above it say which. That's yours to fix, and retrying won't help.(unpacker error)means the pack itself didn't process: size limits, a corrupt pack, or the server's storage having a bad moment. Read the lines above it before deciding which.fatal: unable to access ... 500is the HTTPS transport failing at ref discovery, before any pack goes up. Different layer, often also transient.(Internal Server Error)afterunpack ok, with no other text, is the one in this post. It doesn't look like any policy rejection, and retrying is the first thing to try.
If it doesn't clear in ten or fifteen minutes, the Request ID line is what GitHub support asks for first. Copy it before the terminal scrolls.
Takeaway
When a push is rejected with nothing but "Internal Server Error", don't start bisecting your commits. Check for unpack ok, push a commit the server already has to a throwaway branch, and if that fails too, start a retry loop and walk away. The server will tell you when it's better by accepting the push.
A deploy pipeline that fails in ways the status page doesn't admit to? Send me the details and I'll take a look.
