larksuite/cli · error

dpapi protect failed: %w

Error message

dpapi protect failed: %w

What it means

This error wraps a failure of Windows DPAPI (CryptProtectData) when the keychain encrypts a secret before storing it in the registry. platformSet derives per-service/account entropy, calls dpapiProtect to encrypt the plaintext, and if that OS call fails the error is wrapped and propagated — nothing is written to the registry. The %w keeps the underlying syscall/OLE error for errors.Is/As.

Source

Thrown at internal/keychain/keychain_windows.go:124

	b.Data = nil
	b.Size = 0
}

// platformGet retrieves a value from the Windows registry.
func platformGet(service, account string) (string, error) {
	v, ok := registryGet(service, account)
	if !ok {
		return "", nil
	}
	return v, nil
}

// platformSet stores a value in the Windows registry.
func platformSet(service, account, data string) error {
	entropy := dpapiEntropy(service, account)
	protected, err := dpapiProtect([]byte(data), entropy)
	if err != nil {
		return fmt.Errorf("dpapi protect failed: %w", err)
	}
	return registrySet(service, account, protected)
}

// platformRemove deletes a value from the Windows registry.
func platformRemove(service, account string) error {
	return registryRemove(service, account)
}

// registryGet retrieves a string value from the registry under the given service and account.
func registryGet(service, account string) (string, bool) {
	keyPath := registryPathForService(service)
	k, err := registry.OpenKey(registry.CURRENT_USER, keyPath, registry.QUERY_VALUE)
	if err != nil {
		return "", false
	}
	defer k.Close()

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Read the wrapped cause for the specific DPAPI/Win32 error code.
  2. Ensure the process runs in an interactive user session with a loaded user profile (not a bare service/Session 0 context).
  3. If the user profile or master keys are corrupted, restore DPAPI keys from backup or recreate the user profile.
  4. As a fallback, switch the credential storage backend (e.g. plaintext-with-permissions or OS-independent store) if the CLI supports it.

Example fix

// before: running under a service with no user profile
C:\> sc create svc binPath= lark-cli.exe   // DPAPI fails

// after: run in the user's interactive session
C:\Users\dev> lark-cli auth login
Defensive patterns

Strategy: validation

Validate before calling

// Windows: verify a user profile with DPAPI is available before storing secrets
if !isInteractiveSession() || !userProfileLoaded() {
    return errors.New("DPAPI requires an interactive user session with a loaded profile")
}

Try / catch

if err := keychain.Set(service, account, secret); err != nil {
    if strings.HasPrefix(err.Error(), "dpapi protect failed:") {
        return fmt.Errorf("cannot encrypt credential (profile/DPAPI issue), run in an interactive session: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: platformSet on Windows calls dpapiProtect([]byte(data), entropy) and the CryptProtectData call returns an error — before any registry write occurs.

Common situations: Corrupted user profile or DPAPI master keys; running as a service account/CI without a loaded user profile; credential roaming issues after domain password reset without key backup; running in a container or restricted token lacking DPAPI access.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/ffa557f0d964805f. Report an issue: GitHub.