Skip to content

GPG pinentry terminal conflict: signing fails with "No passphrase given" when using terminal-based pinentry #30539

@Clovel

Description

@Clovel

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

  1. Configure GPG commit signing (git config --global commit.gpgsign true)
  2. Ensure passphrase is not cached (gpgconf --kill gpg-agent)
  3. Start Claude Code
  4. Ask Claude to create a git commit
  5. git commit triggers gpg-agentpinentry-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

  1. Pre-cache passphrase (in a separate terminal): echo "test" | gpg --clearsign > /dev/null
  2. Use --no-gpg-sign: git commit --no-gpg-sign -m "message"
  3. Switch to GUI pinentry: pinentry-program /usr/bin/pinentry-gnome3 in ~/.gnupg/gpg-agent.conf
  4. 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:

  1. Detect that a git command may trigger GPG signing
  2. Pause the Ink renderer (stop reading stdin, reset keyboard modes)
  3. Spawn the command with stdio: ['inherit', 'pipe', 'pipe']
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:basharea:tuibugSomething isn't workinghas reproHas detailed reproduction stepsplatform:wslIssue specifically occurs on WSLstaleIssue is inactive

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions