Summary
When commit.gpgsign=true is configured and the GPG passphrase is not cached, git commit via Claude Code's Bash tool triggers pinentry-curses which cannot read terminal input. The commit fails with gpg: signing failed: No passphrase given.
This affects all terminal-based pinentry variants (pinentry-curses, pinentry-tty) and all interactive TUI programs spawned by the Bash tool.
Steps to Reproduce
- Configure GPG commit signing (
git config --global commit.gpgsign true)
- Ensure passphrase is not cached (
gpgconf --kill gpg-agent)
- Start Claude Code
- Ask Claude to create a git commit
git commit triggers gpg-agent → pinentry-curses launches
Observed Behavior
Pinentry launches but cannot receive keyboard input:
[GNUPG:] PINENTRY_LAUNCHED <PID> curses 1.2.1 /dev/pts/N xterm-256color :0 <UID>/<GID>/5 <GID>/<GID> -
gpg: signing failed: No passphrase given
fatal: failed to write commit object
Key diagnostic facts:
- Pinentry was launched and targeted the correct TTY
GPG_TTY was set correctly (inherited from parent shell)
- Error is
No passphrase given — pinentry could not read user input
Root Cause
Terminal input contention between Claude Code's Ink renderer and pinentry:
Claude Code (Ink renderer) ← reads from process.stdin / terminal
└── Bash Tool (stdio: 'pipe')
└── git commit → gpg-agent → pinentry-curses
└── opens /dev/tty ← ALSO reads from terminal
Both Claude Code's Ink renderer and pinentry-curses read from the same PTY simultaneously. Keystrokes are consumed by the Ink renderer instead of reaching pinentry.
Setting GPG_TTY does not help — the problem is not TTY resolution, it is exclusive terminal input ownership.
Scope
Affects all interactive TUI programs spawned by the Bash tool:
pinentry-curses / pinentry-tty
vim / nano (if git opens an editor)
ssh / sudo password prompts
- Interactive build tools (turborepo, etc.)
Not affected: GUI pinentry (pinentry-gnome3, pinentry-qt, pinentry-mac) since they open their own window.
Workarounds
- Pre-cache passphrase (in a separate terminal):
echo "test" | gpg --clearsign > /dev/null
- Use
--no-gpg-sign: git commit --no-gpg-sign -m "message"
- Switch to GUI pinentry:
pinentry-program /usr/bin/pinentry-gnome3 in ~/.gnupg/gpg-agent.conf
- Increase cache timeout:
default-cache-ttl 86400 in ~/.gnupg/gpg-agent.conf
Proposed Fix
Claude Code should temporarily pause the Ink renderer and release terminal control when a subprocess needs interactive input. For GPG pinentry specifically:
- Detect that a git command may trigger GPG signing
- Pause the Ink renderer (stop reading stdin, reset keyboard modes)
- Spawn the command with
stdio: ['inherit', 'pipe', 'pipe']
- Resume the Ink renderer when the command completes
A workaround plugin (gpg-pinentry-guard) is available in PR #30521.
Environment
- Claude Code 2.1.63
- WSL2 (Linux 6.6.87.2-microsoft-standard-WSL2) / Windows Terminal
- GnuPG 2.4.4, pinentry-curses 1.2.1
Summary
When
commit.gpgsign=trueis configured and the GPG passphrase is not cached,git commitvia Claude Code's Bash tool triggerspinentry-curseswhich cannot read terminal input. The commit fails withgpg: signing failed: No passphrase given.This affects all terminal-based pinentry variants (
pinentry-curses,pinentry-tty) and all interactive TUI programs spawned by the Bash tool.Steps to Reproduce
git config --global commit.gpgsign true)gpgconf --kill gpg-agent)git committriggersgpg-agent→pinentry-curseslaunchesObserved Behavior
Pinentry launches but cannot receive keyboard input:
Key diagnostic facts:
GPG_TTYwas set correctly (inherited from parent shell)No passphrase given— pinentry could not read user inputRoot Cause
Terminal input contention between Claude Code's Ink renderer and pinentry:
Both Claude Code's Ink renderer and pinentry-curses read from the same PTY simultaneously. Keystrokes are consumed by the Ink renderer instead of reaching pinentry.
Setting
GPG_TTYdoes not help — the problem is not TTY resolution, it is exclusive terminal input ownership.Scope
Affects all interactive TUI programs spawned by the Bash tool:
pinentry-curses/pinentry-ttyvim/nano(if git opens an editor)ssh/sudopassword promptsNot affected: GUI pinentry (
pinentry-gnome3,pinentry-qt,pinentry-mac) since they open their own window.Workarounds
echo "test" | gpg --clearsign > /dev/null--no-gpg-sign:git commit --no-gpg-sign -m "message"pinentry-program /usr/bin/pinentry-gnome3in~/.gnupg/gpg-agent.confdefault-cache-ttl 86400in~/.gnupg/gpg-agent.confProposed Fix
Claude Code should temporarily pause the Ink renderer and release terminal control when a subprocess needs interactive input. For GPG pinentry specifically:
stdio: ['inherit', 'pipe', 'pipe']A workaround plugin (
gpg-pinentry-guard) is available in PR #30521.Environment