Skip to content

GPG-signed commits silently fail: bash tool has no TTY for pinentry #3102

Description

@whatnick

Problem

When commit.gpgsign = true is set in the user's global git config, all git commit calls issued by Crush's bash tool fail immediately with:

error: gpg failed to sign the data
fatal: failed to write commit object

The bash tool runs commands in a non-interactive, non-TTY subprocess. GPG's pinentry program requires a TTY to prompt for a passphrase. Because no TTY is available, pinentry exits with an error, GPG fails to sign, and git commit aborts — often without the user realising GPG signing was the cause.

Reproduction

  1. Enable GPG commit signing globally:
    git config --global commit.gpgsign true
    git config --global user.signingkey <KEYID>
  2. Ensure the gpg-agent passphrase is not currently cached (e.g. fresh shell or agent restarted).
  3. Ask Crush to make any commit in a git repository.
  4. Observe the commit fails with gpg failed to sign the data.

Expected Behaviour

One of:

  • Crush detects the GPG failure, surfaces a clear error message, and suggests a workaround (e.g. pre-warm gpg-agent).
  • Crush temporarily exposes a TTY to the subprocess so pinentry can prompt the user.
  • Crush documents / detects commit.gpgsign at session start and warns the user proactively.

Impact

Any user with commit.gpgsign = true (common in enterprise and security-conscious setups) cannot use Crush's commit workflow at all. The failure is silent by default — Crush's bash tool returns a non-zero exit code but the root cause is not obvious.

Suggested Approaches

  1. Pre-warm gpg-agent: Before issuing git commit, run echo test | gpg --clearsign > /dev/null 2>&1 to trigger a passphrase prompt while a TTY is still accessible, caching the passphrase in the agent for subsequent calls.
  2. Detect and surface: Parse the git error output for gpg failed to sign; when detected, emit a user-visible message explaining the cause and the workaround.
  3. SSH signing as documented alternative: Add a note in docs / onboarding that SSH commit signing (gpg.format = ssh + key in ssh-agent) avoids the TTY requirement entirely and works cleanly in non-interactive subprocesses.
  4. Expose TTY for commit step: Where the shell infrastructure allows, attach a PTY to the git commit subprocess so pinentry can function normally.

Workaround

Pre-cache the passphrase before starting a Crush session:

echo "test" | gpg --clearsign > /dev/null

Or switch to SSH-based commit signing:

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub

Related

The identical issue was reported against Kiro (another agentic coding tool): kirodotdev/Kiro#8303 — indicating this is a class-wide problem for AI coding assistants that drive git via non-interactive subprocesses.

Environment

  • OS: Linux (WSL2)
  • gpg-agent running in supervised mode
  • pinentry-mode not set (defaults to ask)

Metadata

Metadata

Assignees

No one assigned

    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