Why VS Code Keeps Asking for Your SSH Passphrase (and How to Fix It)

Summary

A developer reported an issue where VSCode’s Source Control GUI repeatedly prompts for an SSH key passphrase during git pull or git push operations. While the integrated terminal correctly utilizes the SSH agent (requiring the passphrase only once per session), the VSCode UI bypasses the existing agent state, causing a degraded developer experience and workflow interruption.

Root Cause

The discrepancy exists because the Integrated Terminal and the VSCode Extension Host (which handles the Git UI) operate in different execution contexts:

  • Process Isolation: The terminal is a sub-process of the shell environment, which has already inherited the SSH_AUTH_SOCK environment variable.
  • Environment Mismatch: The VSCode GUI processes (the extension host) often fail to inherit the specific environment variables required to communicate with the SSH Agent running in the user’s session.
  • Missing Agent Handshake: Without the SSH_AUTH_SOCK variable being present in the process environment of the VSCode parent process, the Git binary used by the GUI attempts to read the private key directly from the disk rather than asking the agent for the decrypted key.

Why This Happens in Real Systems

In modern Linux distributions (like EndeavourOS) and complex desktop environments, session management is non-trivial:

  • Environment Variable Propagation: When a desktop environment starts, it initializes an agent. However, GUI applications launched via desktop entries (like VSCode) do not always receive the same environment variables as a login shell.
  • Asynchronous Initialization: The SSH agent might be initialized by a shell profile (.zshrc or .bashrc), meaning the variable only exists within that specific shell instance, not the global desktop session.
  • Context Switching: Tools that rely on “magic” background processes (like ssh-agent or gpg-agent) require a consistent, discoverable communication socket across all process trees.

Real-World Impact

  • Developer Friction: Constant manual input of passphrases breaks “flow state” and leads to cognitive fatigue.
  • Tooling Inconsistency: Developers lose trust in the IDE when the terminal works but the GUI fails, leading to “it works in my terminal” debugging loops.
  • Automation Failures: If this behavior occurs in CI/CD environments or automated scripts triggered via GUI-based runners, it can cause silent failures or hanging processes.

Example or Code

To verify the environment mismatch, the developer can compare the socket variable in the terminal versus the VSCode environment.

# Run this in the integrated terminal
echo $SSH_AUTH_SOCK

# Run this in the terminal to check if the agent is actually running
ssh-add -l

To fix this on most Linux systems, ensure the agent is started via the systemd user session or a global profile that GUI processes can access, such as /etc/environment or through a proper XDG Autostart script.

How Senior Engineers Fix It

A senior engineer looks beyond the immediate symptom and addresses the environment lifecycle:

  • Systemd User Services: Instead of relying on shell-based agent starts, implement an ssh-agent.service managed by systemd –user. This ensures the agent is a system-level resident for the user session.
  • Environment Exporting: Use tools like environment.d to ensure SSH_AUTH_SOCK is exported to the entire D-Bus/Systemd session, making it available to all GUI applications.
  • SSH Config Optimization: Ensure AddKeysToAgent yes and UseKeychain yes (on macOS) are correctly configured to automate the handoff once the identity is first unlocked.
  • Global Environment Files: Injecting critical variables into /etc/environment ensures that even processes started by the display manager inherit the necessary hooks.

Why Juniors Miss It

  • Symptom-Focused Debugging: Juniors often try to fix the application (VSCode) or the tool (Git) rather than the underlying environment (the Shell/Session).
  • Terminal Bias: There is a tendency to assume that if git works in the terminal, the system is “fine,” failing to realize that environment variables are not global—they are process-specific.
  • Lack of Process Awareness: Juniors often overlook the distinction between a Login Shell, an Interactive Shell, and a GUI Process, which all have different ways of inheriting environmental state.

Leave a Comment