larksuite/cli · error

failed to verify user identity: %w

Error message

failed to verify user identity: %w

What it means

A UAT was resolved and the HTTP client built, but the user_info API call used to verify the token and resolve the user's identity failed. Because holding a UAT makes verification mandatory (security requirement), the failure is wrapped and propagated; callers in doResolveAccount treat enrichment failure as non-fatal only in some paths, clearing UserOpenId/UserName for safety. The wrapped cause distinguishes network errors from API rejections (expired/revoked token, insufficient scopes).

Source

Thrown at internal/credential/credential_provider.go:248

	if err != nil {
		var blockErr *extcred.BlockError
		if errors.As(err, &blockErr) {
			return nil // provider explicitly blocks UAT; skip enrichment
		}
		return fmt.Errorf("failed to resolve UAT for user identity verification: %w", err)
	}
	if !found {
		return nil
	}
	// Have UAT — must verify and resolve identity
	hc, err := p.httpClient()
	if err != nil {
		return fmt.Errorf("failed to get HTTP client for user_info: %w", err)
	}
	requestCtx := core.WithCredentialSource(ctx, tok.Source)
	info, err := fetchUserInfo(requestCtx, hc, acct.Brand, tok.Token)
	if err != nil {
		return fmt.Errorf("failed to verify user identity: %w", err)
	}
	acct.UserOpenId = info.OpenID
	acct.UserName = info.Name
	return nil
}

func (p *CredentialProvider) selectedCredentialSource(ctx context.Context) (credentialSource, error) {
	if p.selectedSource != nil {
		return p.selectedSource, nil
	}
	if p.defaultAcct == nil {
		return nil, nil
	}
	if _, err := p.ResolveAccount(ctx); err != nil {
		return nil, err
	}
	if p.selectedSource == nil {
		return nil, fmt.Errorf("credential provider resolved an account without selecting a token source")

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Read the wrapped cause: if it indicates 401/invalid token, re-authenticate (re-run the login/auth flow) to mint a fresh UAT
  2. If it indicates missing scopes, grant the required user scopes to the app in the Lark/Feishu admin console and re-auth
  3. If it is a network error, fix connectivity/proxy to the accounts endpoint and retry
  4. If enrichment is non-fatal for your flow, handle the error by clearing UserOpenId/UserName rather than aborting, as doResolveAccount does in its tolerant path

Example fix

var infoErr *errs.APIError
if errors.As(err, &infoErr) && infoErr.Code == 401 {
    // UAT expired/invalid — trigger re-login instead of retrying
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: only proceed with UAT flows when the token is present and fresh
if tok, found, err := source.TryResolveToken(ctx, credential.TokenSpec{Type: credential.TokenTypeUAT, AppID: appID}); err == nil && found && !tokenExpired(tok.Token) {
	// safe to verify identity
}

Type guard

func isAPIError(err error) bool {
	var ae *errs.APIError
	return errors.As(err, &ae)
}

Try / catch

if err := enrichUserInfo(ctx, acct, source); err != nil {
	var apiErr *errs.APIError
	if errors.As(err, &apiErr) && (apiErr.HTTPStatus == 401 || apiErr.HTTPStatus == 403) {
		// token expired/revoked or scopes missing — trigger re-login
	} else {
		// transport failure — safe to retry with backoff
	}
}

Prevention

When it happens

Trigger: fetchUserInfo(requestCtx, hc, acct.Brand, tok.Token) returns an error — the user_info endpoint returned an error (401/403, token expired or revoked, missing scopes) or the request failed at the transport level.

Common situations: UAT expired since being cached; user revoked the app; tenant admin removed required user scopes; network/firewall blocking the accounts endpoint; brand endpoint misconfigured.

Related errors


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