SOLVED: gpg signing failed: inappropriate ioctl for device
The error "gpg signing failed: inappropriate ioctl for device" appears when running git commit with GPG commit signing enabled. It is especially common inside SSH sessions, tmux panes, screen sessions, or any terminal that is not a direct interactive TTY. This guide explains why it happens and provides three reliable fixes.
The Full Error Message
When you run git commit, git calls GPG to sign the commit. If GPG cannot open a pinentry dialog to ask for your passphrase, the commit fails with:
error: gpg failed to sign the data
fatal: failed to write commit object
Running GPG directly reveals the underlying cause:
echo "test" | gpg --clearsign
gpg: signing failed: Inappropriate ioctl for device
gpg: [stdin]: clear-sign failed: Inappropriate ioctl for device
Why This Happens
GPG's pinentry program needs a TTY (a real terminal device) to display the passphrase prompt. When you connect over SSH, run commands inside tmux or screen, or invoke GPG from a script, the process may not have a TTY attached — or the TTY variable may not be set correctly.
The GPG_TTY environment variable tells GPG which TTY to use for pinentry. When it is missing or set to an invalid value, GPG cannot open the dialog and fails with "Inappropriate ioctl for device".
Fix 1: Set GPG_TTY (Recommended)
This is the most common and most reliable fix.
Apply immediately
export GPG_TTY=$(tty)
Test it works:
echo "test" | gpg --clearsign
git commit --allow-empty -m "test gpg signing"
Make it permanent
Add the export to your shell startup file so it applies to every new session.
For Bash — add to ~/.bashrc:
export GPG_TTY=$(tty)
For Zsh — add to ~/.zshrc:
export GPG_TTY=$(tty)
Reload the file:
source ~/.bashrc # or source ~/.zshrc
Why this works inside tmux
tmux multiplexes terminals. Each pane has its own pseudo-TTY, but the GPG_TTY variable from a parent shell is not automatically updated when you open a new pane. Running export GPG_TTY=$(tty) inside the pane sets it to the correct device for that pane.
If you use tmux heavily, add the export to your ~/.bashrc or ~/.zshrc (not just ~/.bash_profile) so it runs for every new shell, including non-login shells opened by tmux.
Fix 2: Restart gpg-agent
A stale or crashed gpg-agent process can also cause this error, particularly after the agent has been running for a long time or after a system resume from sleep.
gpgconf --kill gpg-agent
gpgconf --launch gpg-agent
Then retry your commit:
git commit
If you want to confirm the agent is running after the restart:
gpgconf --list-dirs agent-socket
gpg-agent --version
Fix 3: Use pinentry-mode loopback
The loopback pinentry mode tells GPG to ask for the passphrase directly on the command line rather than opening a graphical or curses-based pinentry dialog. This always works in headless or non-interactive environments because it does not require a real TTY for a popup.
Step 1 — Configure gpg.conf
Add the following line to ~/.gnupg/gpg.conf (create the file if it does not exist):
pinentry-mode loopback
Step 2 — Configure gpg-agent.conf
Add the following line to ~/.gnupg/gpg-agent.conf (create the file if it does not exist):
allow-loopback-pinentry
Step 3 — Reload gpg-agent
gpgconf --kill gpg-agent
The agent will restart automatically on the next GPG operation. Retry your commit:
git commit
Trade-off
With loopback mode, GPG prompts for the passphrase in the terminal rather than using a separate pinentry window. This is slightly less secure in some threat models (the passphrase passes through the controlling terminal rather than a dedicated secure input channel) but is perfectly acceptable for most development environments.
Verifying git commit signing is configured
Before applying the fixes above, confirm that GPG signing is actually enabled in your git configuration and that the signing key is correct:
# Check if commit signing is enabled
git config --global commit.gpgsign
# Check which key is configured
git config --global user.signingkey
# List your available secret keys
gpg --list-secret-keys --keyid-format LONG
If commit.gpgsign is true and the key ID matches one of your secret keys, the issue is the TTY/pinentry problem described in this article.
If you want to disable GPG signing entirely for a single repository:
git config commit.gpgsign false
Quick Recommendation Table
| Scenario | Recommended Fix |
|---|---|
| Interactive SSH session, tmux, screen | Fix 1: export GPG_TTY=$(tty) in shell rc |
| Stale agent after sleep or long uptime | Fix 2: restart gpg-agent |
| Headless CI / no TTY at all | Fix 3: pinentry-mode loopback |
| One-off test without changing config | Run export GPG_TTY=$(tty) in the current shell |
| Permanent fix for all environments | Fix 1 + Fix 3 together |