This error occurs when Git cannot use GPG to cryptographically sign a tag. Common causes include missing GPG_TTY configuration, expired keys, or misconfigured pinentry programs.
When you create a signed Git tag using `git tag -s` or `git tag -u <key-id>`, Git invokes GPG (GNU Privacy Guard) to create a cryptographic signature. This signature proves that the tag was created by someone with access to the private GPG key, providing authenticity and integrity verification for releases. The "gpg failed to sign the data" error indicates that the signing process failed at the GPG level. This can happen for several reasons: GPG cannot find the correct private key, the key has expired, the passphrase entry (pinentry) program cannot communicate with your terminal, or the GPG agent has crashed or become unresponsive. Unlike unsigned tags which are simple pointers to commits, signed tags create tag objects that include the tagger's identity, a timestamp, a message, and the cryptographic signature. This extra security is especially important for software releases, as it allows users to verify that a tagged version came from a trusted source.
The most common fix is to set the GPG_TTY variable so GPG knows which terminal to use for passphrase entry:
export GPG_TTY=$(tty)Add this to your shell configuration file for persistence:
# For bash
echo 'export GPG_TTY=$(tty)' >> ~/.bashrc
# For zsh
echo 'export GPG_TTY=$(tty)' >> ~/.zshrcThen reload your shell configuration:
source ~/.bashrc # or ~/.zshrcBefore troubleshooting Git, verify that GPG itself can sign data:
echo "test" | gpg --clearsignIf this command:
- Works and shows signed output: GPG is functioning; the issue is Git configuration
- Hangs: The pinentry program cannot display a prompt
- Shows "no default secret key": No signing key is configured
- Shows "unusable secret key": The key may be expired or corrupted
List your GPG keys and check their expiration dates:
gpg --list-keys --keyid-format LONGLook for [expired] next to your key. If expired, extend the expiration date:
gpg --edit-key YOUR_KEY_ID
gpg> expire
# Follow prompts to set a new expiration date
gpg> saveAfter extending, you may need to re-upload your public key to GitHub/GitLab.
Ensure Git is configured to use the correct GPG program and signing key:
# Check current configuration
git config --global user.signingkey
git config --global gpg.program
# List available secret keys to find your key ID
gpg --list-secret-keys --keyid-format LONGThe key ID is the hex string after the algorithm (e.g., rsa4096/3AA5C34371567BD2). Set it in Git:
git config --global user.signingkey YOUR_KEY_ID
git config --global gpg.program gpgOn some Linux systems, you may need gpg2 instead of gpg:
git config --global gpg.program gpg2A hung or crashed GPG agent can cause signing failures. Restart it:
gpgconf --kill gpg-agentOr more forcefully:
pkill -9 gpg-agentThe agent will automatically restart on the next GPG operation. You can also manually start it:
gpg-agent --daemonIf you're in an SSH session or headless environment, you need a terminal-based pinentry:
On Linux (Debian/Ubuntu):
sudo apt install pinentry-tty
sudo update-alternatives --config pinentry
# Select pinentry-tty from the listOr configure manually:
echo "pinentry-program /usr/bin/pinentry-tty" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agentOn macOS with Homebrew:
brew install pinentry-mac
echo "pinentry-program $(which pinentry-mac)" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agentIf the issue persists, enable Git tracing to see exactly what's happening:
GIT_TRACE=1 git tag -s v1.0.0 -m "Test tag"For even more detail on the GPG interaction:
GIT_TRACE=1 GIT_TRACE_SETUP=1 git tag -s v1.0.0 -m "Test tag" 2>&1 | head -50This will show the exact GPG command Git is running and any error output.
Once GPG signing is working, create your signed tag:
git tag -s v1.0.0 -m "Release version 1.0.0"Verify the signature:
git tag -v v1.0.0You should see "Good signature from" followed by your key identity.
### CI/CD and Automated Signing
In CI/CD pipelines, you typically cannot enter a passphrase interactively. Options include:
1. Use a key without a passphrase (less secure):
gpg --batch --passphrase '' --quick-gen-key "CI Bot <[email protected]>" default default never2. Pre-cache the passphrase using gpg-preset-passphrase:
echo "allow-preset-passphrase" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent
/usr/lib/gnupg/gpg-preset-passphrase --preset KEYGRIP <<< "your-passphrase"3. Use SSH signing (Git 2.34+) which can be easier to configure in CI:
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub### Windows-Specific Issues
On Windows, if you installed GPG separately from Git for Windows:
1. Git may use its bundled GPG while your keys are in a different GPG installation
2. Add Git's GPG to your PATH: C:\Program Files\Git\usr\bin
3. Or point Git to your GPG: git config --global gpg.program "C:/Program Files (x86)/GnuPG/bin/gpg.exe"
### Hardware Security Keys (YubiKey)
If using a YubiKey or other hardware key:
1. Ensure the key is inserted and recognized: gpg --card-status
2. Touch may be required for each signing operation
3. The pinentry must support hardware token prompts
### Automatic Tag Signing
To automatically sign all annotated tags:
git config --global tag.gpgSign truewarning: BOM detected in file, this may cause issues
UTF-8 Byte Order Mark (BOM) detected in file
fatal: Server does not support --shallow-exclude
Server does not support --shallow-exclude
warning: filtering out blobs larger than limit
Git partial clone filtering large blobs warning
fatal: Server does not support --shallow-since
Server does not support --shallow-since in Git
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server