}

SOLVED: gpg signing failed: inappropriate ioctl for device

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

ScenarioRecommended Fix
Interactive SSH session, tmux, screenFix 1: export GPG_TTY=$(tty) in shell rc
Stale agent after sleep or long uptimeFix 2: restart gpg-agent
Headless CI / no TTY at allFix 3: pinentry-mode loopback
One-off test without changing configRun export GPG_TTY=$(tty) in the current shell
Permanent fix for all environmentsFix 1 + Fix 3 together

Related Articles

Leonardo Lazzaro

Software engineer and technical writer. 10+ years experience in DevOps, Python, and Linux systems.

More articles by Leonardo Lazzaro