How to Fix 'Error Storing Credentials' in Docker Login on WSL2
The Docker login "error storing credentials" on WSL2 occurs because Docker tries to use docker-credential-desktop.exe (the Windows-side credential helper), and the cross-OS pipe between WSL2 and Windows fails or the helper returns malformed data. The fix is to change the credsStore setting in your Docker config so that credentials are stored by a helper that works inside your WSL2 distro. The fastest reliable fix is switching to docker-credential-pass. For non-sensitive dev environments, you can also remove credsStore entirely so that Docker falls back to base64-encoded storage in ~/.docker/config.json.
Quick Fix: Remove the Broken Credential Store Entry
Open your Docker config file and either delete or empty the credsStore key. This tells Docker to store credentials as base64-encoded strings in the config file itself. That's fine for local development, but not for shared or production machines.
# Back up the existing config before editing.
cp ~/.docker/config.json ~/.docker/config.json.bak
# Replace the credsStore value with an empty string.
cat ~/.docker/config.json | \
python3 -c "import sys,json; c=json.load(sys.stdin); c['credsStore']=''; print(json.dumps(c,indent=2))" \
> /tmp/docker-config.json && mv /tmp/docker-config.json ~/.docker/config.json
# Verify the login works now.
docker login -u your-username
Proper Fix: Use docker-credential-pass on WSL2
If you want encrypted credential storage inside WSL2 (and you should on any shared machine), install pass and the Docker credential helper that wraps it. This approach stores credentials in a GPG-encrypted store entirely within Linux, bypassing the Windows pipe that causes the error.
# Install the pass password manager and gpg.
sudo apt-get update && sudo apt-get install -y pass gnupg2
# Generate a GPG key (follow the prompts; use a passphrase you'll remember).
gpg2 --gen-key
# Note the key ID from the output, e.g. "ABC123...".
# Initialize the password store with your key ID.
pass init "YOUR_GPG_KEY_ID"
# Download the latest docker-credential-pass binary.
DCPASS_VERSION="v0.8.2"
curl -fsSL "https://github.com/docker/docker-credential-helpers/releases/download/${DCPASS_VERSION}/docker-credential-pass-${DCPASS_VERSION}.linux-amd64" \
-o docker-credential-pass
# Make it executable and move it onto your PATH.
chmod +x docker-credential-pass
sudo mv docker-credential-pass /usr/local/bin/
# Point Docker at the pass credential helper.
cat ~/.docker/config.json | \
python3 -c "import sys,json; c=json.load(sys.stdin); c['credsStore']='pass'; print(json.dumps(c,indent=2))" \
> /tmp/docker-config.json && mv /tmp/docker-config.json ~/.docker/config.json
# Test the login.
docker login -u your-username
Why Docker Login Credential Storage Fails on WSL2
Docker Desktop for Windows installs a credential helper called docker-credential-desktop.exe that talks to the Windows Credential Manager. When you run docker login inside a WSL2 distro, Docker reads ~/.docker/config.json, sees "credsStore": "desktop", and tries to invoke docker-credential-desktop.exe through Windows interop. This cross-OS call is fragile. It breaks when the Docker Desktop backend isn't running, when the WSL interop layer hiccups, or when the Windows Credential Manager returns data that the Linux-side client can't parse. The exact error message varies. You might see "error storing credentials - err: exit status 1, out: stub received bad data" or sometimes "error getting credentials" on pull commands.
Gotchas and Edge Cases
Docker Desktop overwrites your config. Restarting Docker Desktop can reset ~/.docker/config.json and re-inject "credsStore": "desktop". If this keeps happening, disable the Docker Desktop WSL integration for your distro (Settings → Resources → WSL Integration) and install the Docker Engine directly inside WSL2 instead. This is the more stable long-term setup for serious development.
credsStore vs credStore vs credHelpers. The correct key is credsStore (with an 's'). A common mistake is editing the wrong key. credHelpers is a per-registry map, which is useful if you want pass for Docker Hub but a different helper for ECR or GCR.
GPG agent timeouts. If you use the pass approach and your GPG key has a passphrase, the GPG agent caches the passphrase for a limited time (10 minutes by default). After that window, docker pull hangs waiting for a passphrase prompt on a TTY that doesn't exist. You can extend the cache timeout in ~/.gnupg/gpg-agent.conf.
# Extend GPG agent passphrase cache to 8 hours (28800 seconds).
mkdir -p ~/.gnupg
echo "default-cache-ttl 28800" >> ~/.gnupg/gpg-agent.conf
echo "max-cache-ttl 28800" >> ~/.gnupg/gpg-agent.conf
# Reload the agent to pick up changes.
gpgconf --kill gpg-agent
Which Credential Storage Approach to Pick
For a personal dev laptop where you're the only user, clearing credsStore to use plaintext storage is the pragmatic choice. It takes ten seconds and never breaks. For shared CI boxes, team dev environments, or any situation where credentials leaking from a flat file would be a problem, set up docker-credential-pass. If you're using AWS ECR, GCR, or ACR, skip both and use the cloud-specific credential helpers (docker-credential-ecr-login, for example). These handle token rotation automatically and sidestep this problem entirely.