docker/cli · error
error saving credentials
Error message
error saving credentials: %v
What it means
Returned by storeCredentials (registry/login.go:334) wrapping the error from the credentials store's Store method. After a successful registry login the CLI persists the auth config via the configured credentials helper (file, osxkeychain, wincred, pass, etc.); any failure there is wrapped here. Note it uses %v, so the chain is not unwrappable via errors.Is.
Solutions
- Install/verify the configured credential helper binary (e.g. docker-credential-osxkeychain) is on PATH.
- Check permissions and ownership of ~/.docker/config.json and ~/.docker/.
- Fix or remove a broken `credsStore`/`credHelpers` entry in config.json if you do not need a native helper.
- Unlock the keychain (macOS) or unlock the pass store (Linux) and retry.
Example fix
// before: config.json has "credsStore": "osxkeychain" but binary missing // after: install helper and retry brew install docker-credential-helper # or ship the binary on PATH docker login
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: ensure the configured credential helper binary is present.
func ensureCredHelper(cfg *configfile.ConfigFile) error {
helper := cfg.CredentialsStore
if helper != "" {
if _, err := exec.LookPath("docker-credential-" + helper); err != nil {
return fmt.Errorf("credential helper %q not found on PATH", helper)
}
}
return nil
} Try / catch
// Distinguish store failures from auth failures; do not leak credentials.
if err := storeCredentials(cfg, auth); err != nil {
if strings.Contains(err.Error(), "error saving credentials") {
log.Printf("login ok but credential store failed: %v", err)
// advise user to fix credsStore/credHelpers, do not retry blindly
}
return err
} Prevention
- Install the docker-credential-* helper matching your credsStore setting.
- Keep ~/.docker/config.json writable and correctly owned.
- Avoid %v-only wrapping if you need errors.Is matching (this error uses %v).
When it happens
Trigger: Login succeeds against the registry but writing to ~/.docker/config.json or the native credential helper fails. Common with `credsStore` set to a helper binary that is missing, broken, or locked (e.g. docker-credential-osxkeychain on a locked keychain).
Common situations: Missing docker-credential-* binary on PATH, keychain locked on macOS, file permission/ownership issues on ~/.docker/config.json, full disk, or a misspelled credsStore/credHelpers entry in config.json.
Related errors
- error: username is required
- error: password is required
- conflicting options: cannot specify both --password and…
- the --password-stdin option requires --username to be set
- username is empty
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/71bd2d370067dbb6.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/registry/login.go:334
return "", err
}
return response.Auth.Status, nil
}
func storeCredentials(cfg *configfile.ConfigFile, authConfig registrytypes.AuthConfig) error {
creds := cfg.GetCredentialsStore(authConfig.ServerAddress)
if err := creds.Store(configtypes.AuthConfig{
Username: authConfig.Username,
Password: authConfig.Password,
ServerAddress: authConfig.ServerAddress,
// TODO(thaJeztah): Are these expected to be included?
Auth: authConfig.Auth,
IdentityToken: authConfig.IdentityToken,
RegistryToken: authConfig.RegistryToken,
}); err != nil {
return fmt.Errorf("error saving credentials: %v", err)
}
return nil
}
func loginWithRegistry(ctx context.Context, apiClient client.SystemAPIClient, options client.RegistryLoginOptions) (client.RegistryLoginResult, error) {
res, err := apiClient.RegistryLogin(ctx, options)
if err != nil {
if client.IsErrConnectionFailed(err) {
// daemon isn't responding; attempt to login client side.
return loginClientSide(ctx, options)
}
return client.RegistryLoginResult{}, err
}
return res, nil
}
View on GitHub (pinned to 4f84911bfe)