Fixing SSH Agent Access in Non-Interactive Shells for Claude Code
Summary
Section titled “Summary”When using AI coding assistants like Claude Code to execute Git operations over SSH, you may encounter authentication failures due to SSH agent not being accessible in non-interactive shells. This article documents the root cause analysis and provides a complete solution that works for both interactive terminal usage and automated tools.
Problem: git commit and git push fail with “Couldn’t get agent socket” error when executed by Claude Code’s Bash tool.
Solution: Configure SSH agent socket discovery before the interactive shell check in ~/.bashrc, combined with automatic agent startup and key loading for interactive sessions.
Problem Description
Section titled “Problem Description”When Claude Code (or any tool using non-interactive shells) attempts to execute Git commands that require SSH authentication, the following error occurs:
error: Couldn't get agent socket?fatal: failed to write commit objectHowever, the same commands work perfectly fine when executed directly in an interactive terminal.
Environment Information
Section titled “Environment Information”- OS: WSL2 (Ubuntu on Windows)
- Shell: Bash
- Git Remote: SSH protocol (
git@github.com:username/repo.git) - Tool: Claude Code (uses non-interactive Bash shells)
- SSH Key: Ed25519 key located at
~/.ssh/
Root Cause Analysis
Section titled “Root Cause Analysis”Interactive vs Non-Interactive Shells
Section titled “Interactive vs Non-Interactive Shells”The standard ~/.bashrc configuration includes this check near the beginning:
# If not running interactively, don't do anythingcase $- in *i*) ;; *) return;;esacWhat this does:
- Checks if the current shell is interactive (by looking for ‘i’ in shell flags)
- If non-interactive, immediately returns, preventing all subsequent configuration from executing
The Problem
Section titled “The Problem”Most SSH agent configurations are placed after this check in ~/.bashrc:
# ~/.bashrc (line ~120+)if [ -z "$SSH_AUTH_SOCK" ]; then eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_*fiResult: Non-interactive shells never reach the SSH agent configuration, so SSH_AUTH_SOCK is never set.
Claude Code’s Behavior
Section titled “Claude Code’s Behavior”Claude Code’s Bash tool:
- Creates non-interactive shells for each command execution
- Each execution is an independent session
- Does not inherit environment variables from the user’s terminal
- Sources
~/.bashrcbut exits early due to the interactive check
Investigation Process
Section titled “Investigation Process”Step 1: Confirm the Interactive Check Exists
Section titled “Step 1: Confirm the Interactive Check Exists”$ head -10 ~/.bashrcOutput confirmed the presence of:
case $- in *i*) ;; *) return;;esacStep 2: Check Shell Flags
Section titled “Step 2: Check Shell Flags”$ echo $-# Interactive: himBHs (contains 'i')# Non-interactive: hmtBc (no 'i')Step 3: Verify SSH_AUTH_SOCK in Non-Interactive Shell
Section titled “Step 3: Verify SSH_AUTH_SOCK in Non-Interactive Shell”$ bash -c 'echo "SSH_AUTH_SOCK: $SSH_AUTH_SOCK"'# Output: SSH_AUTH_SOCK: (empty)Step 4: Check for Existing SSH Agent
Section titled “Step 4: Check for Existing SSH Agent”$ find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null# Output: (empty - no agent running)Step 5: Test SSH Agent Connectivity
Section titled “Step 5: Test SSH Agent Connectivity”$ ssh-add -l# Output: Could not open a connection to your authentication agent.Step 6: Verify Git Uses SSH
Section titled “Step 6: Verify Git Uses SSH”$ git remote -vConclusion: The problem exists - non-interactive shells cannot access SSH agent, and Git operations over SSH will fail.
Solution Implementation
Section titled “Solution Implementation”The solution involves configuring SSH agent in two places in ~/.bashrc:
Part 1: Non-Interactive Shell Support
Section titled “Part 1: Non-Interactive Shell Support”Add this before the case $- interactive check (around line 5):
# === SSH Agent Configuration (for non-interactive shells) ===if [ -z "$SSH_AUTH_SOCK" ]; then export SSH_AUTH_SOCK=$(find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null | head -1)fi# ============================================================Purpose:
- Allows non-interactive shells to find and use existing SSH agent sockets
- Executes before the shell returns
- Lightweight - only exports an environment variable
Part 2: Interactive Shell Support
Section titled “Part 2: Interactive Shell Support”Add this after the case $- check (around line 120+):
# === SSH Agent Auto-start (Interactive Shells) ===# Check if SSH agent is already runningif [ -z "$SSH_AUTH_SOCK" ]; then # Try to find existing agent socket EXISTING_SOCK=$(find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null | head -1) if [ -n "$EXISTING_SOCK" ]; then export SSH_AUTH_SOCK="$EXISTING_SOCK" else # Start a new agent if none exists eval "$(ssh-agent -s)" >/dev/null fifi
# Add SSH keys if agent is running and keys are not loadedif [ -n "$SSH_AUTH_SOCK" ]; then ssh-add -l >/dev/null 2>&1 if [ $? -eq 1 ]; then # Agent has no identities, add keys for key in ~/.ssh/id_*; do if [ -f "$key" ] && [[ "$key" != *.pub ]]; then ssh-add "$key" 2>/dev/null fi done fifi# =====================================================Purpose:
- Automatically starts SSH agent when opening a new terminal (if not already running)
- Automatically loads all SSH private keys from
~/.ssh/ - Avoids duplicate key additions
- Reuses existing agent across terminal sessions
Complete Implementation
Section titled “Complete Implementation”Edit ~/.bashrc:
# ~/.bashrc: executed by bash(1) for non-login shells.
# === SSH Agent Configuration (for non-interactive shells) ===if [ -z "$SSH_AUTH_SOCK" ]; then export SSH_AUTH_SOCK=$(find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null | head -1)fi# ============================================================
# If not running interactively, don't do anythingcase $- in *i*) ;; *) return;;esac
# ... (rest of standard bashrc configuration) ...
# === SSH Agent Auto-start (Interactive Shells) ===if [ -z "$SSH_AUTH_SOCK" ]; then EXISTING_SOCK=$(find /tmp -type s -path "*ssh*agent*" -user "$USER" 2>/dev/null | head -1) if [ -n "$EXISTING_SOCK" ]; then export SSH_AUTH_SOCK="$EXISTING_SOCK" else eval "$(ssh-agent -s)" >/dev/null fifi
if [ -n "$SSH_AUTH_SOCK" ]; then ssh-add -l >/dev/null 2>&1 if [ $? -eq 1 ]; then for key in ~/.ssh/id_*; do if [ -f "$key" ] && [[ "$key" != *.pub ]]; then ssh-add "$key" 2>/dev/null fi done fifi# =====================================================Verification
Section titled “Verification”Test 1: Start SSH Agent Manually
Section titled “Test 1: Start SSH Agent Manually”$ eval "$(ssh-agent -s)"# Output: Agent pid 2245478
$ ssh-add ~/.ssh/id_ed25519# Output: Identity added: /home/username/.ssh/id_ed25519 (user@example.com)Test 2: Verify Non-Interactive Shell Access
Section titled “Test 2: Verify Non-Interactive Shell Access”$ bash -c 'source ~/.bashrc; echo "SSH_AUTH_SOCK: $SSH_AUTH_SOCK"; ssh-add -l'# Output:# 256 SHA256:xxx... user@example.com (ED25519)✅ Success: Non-interactive shells can now access the SSH agent!
Test 3: Verify Interactive Shell Auto-Start
Section titled “Test 3: Verify Interactive Shell Auto-Start”$ bash -i -c 'ssh-add -l'# Output: 256 SHA256:xxx... user@example.com (ED25519)✅ Success: Keys are automatically loaded in new interactive sessions!
Conclusion
Section titled “Conclusion”Problem Summary
Section titled “Problem Summary”AI coding assistants like Claude Code use non-interactive shells that cannot access SSH agent when the configuration is placed after the interactive check in ~/.bashrc.
Solution Summary
Section titled “Solution Summary”Configure SSH agent in two parts:
- Before interactive check: Export
SSH_AUTH_SOCKby finding existing agent sockets - After interactive check: Auto-start agent and load keys for interactive sessions
Benefits
Section titled “Benefits”- ✅ Interactive terminals: Automatic SSH agent startup and key loading
- ✅ Non-interactive shells: Automatic SSH agent socket discovery
- ✅ Claude Code: Can execute SSH-authenticated Git operations
- ✅ CI/CD pipelines: Can access SSH agent (if socket exists)
- ✅ Multiple terminals: Share the same SSH agent instance
Affected Scenarios
Section titled “Affected Scenarios”Works Now ✅:
- Claude Code executing
git commit,git push,git pull - Shell scripts using SSH/Git
- Automated deployment scripts
- Any tool using non-interactive Bash shells
Always Worked ✅:
- Manual Git operations in terminal
- Interactive SSH connections
- Standard development workflow
Technical Notes
Section titled “Technical Notes”- This approach is lightweight and safe
- Only environment variables are exported before the interactive check
- No side effects or performance impact
- Compatible with WSL, Linux, and macOS
- Works with any SSH-based tool (Git, rsync, scp, etc.)
Additional Resources
Section titled “Additional Resources”- Bash Startup Files Documentation
- SSH Agent Forwarding Guide
- Git SSH Configuration
- Claude Code Documentation
Author’s Note: This solution was developed while troubleshooting Claude Code’s Git integration in a WSL2 environment. The investigation process and solution may be helpful for anyone using AI coding assistants or automated tools that execute Git commands via SSH.