The 2 AM Commit Fail
It’s late. You’ve just finished a grueling session squashing a critical bug. You type git commit -m "Fix: resolve race condition" and hit Enter, expecting a success message. Instead, Git throws a brick wall at you:
error: gpg failed to sign the data
fatal: failed to write commit object
This error is frustrating because it’s vague. It rarely means your code is wrong. Usually, it means Git tried to call GPG to sign your work, but GPG couldn't find a way to ask you for your passphrase. It’s a communication breakdown between your terminal and the encryption engine.
Why GPG is Ghosting You
Git doesn't handle the password prompt itself; it hands that job off to an agent. Here is why that handoff usually fails:
- Missing TTY: GPG doesn't know which terminal screen to use for the password prompt.
- Stale Agent: The
gpg-agentis hanging or crashed in the background. - Path Mismatch: You have multiple GPG versions installed, and Git is calling the wrong one.
- Expired Keys: Your GPG key reached its end-of-life date (commonly 1–2 years after creation).
The "I Need This Pushed Now" Hotfix
If you are in a rush, you can often kickstart the system by manually pointing GPG to your current terminal and restarting the background daemon. Run these three commands:
export GPG_TTY=$(tty)
gpgconf --kill gpg-agent
gpg-agent --daemon
Try your commit again. It should now trigger the popup or terminal prompt for your passphrase. If it works, great—but you’ll likely need the permanent fixes below to stop it from happening again tomorrow.
Permanent Solutions
1. Automate the GPG_TTY Variable
This is the #1 fix for Linux and macOS users. You need to tell your shell to always define the display terminal for GPG. Open your profile configuration (~/.zshrc for Mac/Zsh or ~/.bashrc for Bash) and add this line at the bottom:
export GPG_TTY=$(tty)
Save the file and refresh your session by running source ~/.zshrc.
2. Configure Pinentry for macOS and Linux
GPG uses a small utility called pinentry to show the password box. On macOS, Homebrew often installs this in a location GPG doesn't check by default. You need to be explicit.
Create or edit ~/.gnupg/gpg-agent.conf:
# For Apple Silicon Macs (M1/M2/M3)
pinentry-program /opt/homebrew/bin/pinentry-mac
# For Intel Macs
# pinentry-program /usr/local/bin/pinentry-mac
# For Linux (Server/Headless)
# pinentry-program /usr/bin/pinentry-curses
After saving, restart the agent: gpgconf --kill gpg-agent.
3. Validate Your Key Expiration
Sometimes the configuration is perfect, but the key itself is dead. Check your key status with this command:
gpg --list-secret-keys --keyid-format LONG
Look for a line like sec rsa4096/3AA5C34371567BD2 2022-01-01 [SC] [expired: 2024-01-01]. If you see "expired," you must extend it:
gpg --edit-key 3AA5C34371567BD2
gpg> expire
# Follow the prompts to add 1y or 2y
gpg> save
4. Windows-Specific Fix (Git Bash)
On Windows, Git often bundles its own GPG, which might clash with Gpg4win. Force Git to use the correct version by pointing directly to the executable:
git config --global gpg.program "C:\Program Files (x86)\GnuPG\bin\gpg.exe"
Final Verification
Test the pipeline by forcing a signed commit on a dummy change:
git commit -S -m "Testing GPG fix"
Check the results with git log --show-signature -1. If you see gpg: Good signature, your setup is officially repaired. You can now get back to shipping code without the 2 AM headache.

