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

  1. Install/verify the configured credential helper binary (e.g. docker-credential-osxkeychain) is on PATH.
  2. Check permissions and ownership of ~/.docker/config.json and ~/.docker/.
  3. Fix or remove a broken `credsStore`/`credHelpers` entry in config.json if you do not need a native helper.
  4. 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

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


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)