larksuite/cli · error

user_info API returned HTTP %d

Error message

user_info API returned HTTP %d

What it means

fetchUserInfo (for the authen/v1/user_info endpoint, called from enrichUserInfo) requires HTTP 200; any other status aborts with this plain error before decoding JSON. It signals the user-info HTTP call itself failed at the transport/gateway level, not an API-level business error.

Source

Thrown at internal/credential/user_info.go:38

// fetchUserInfo calls /open-apis/authen/v1/user_info with a UAT to get the user's identity.
func fetchUserInfo(ctx context.Context, httpClient *http.Client, brand core.LarkBrand, uat string) (*userInfo, error) {
	ep := core.ResolveEndpoints(brand)
	url := ep.Open + "/open-apis/authen/v1/user_info"

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+uat)

	resp, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("user_info API returned HTTP %d", resp.StatusCode)
	}

	var result struct {
		Code int    `json:"code"`
		Msg  string `json:"msg"`
		Data struct {
			OpenID string `json:"open_id"`
			Name   string `json:"name"`
		} `json:"data"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}
	if result.Code != 0 {
		return nil, fmt.Errorf("user_info API error: [%d] %s", result.Code, result.Msg)
	}
	return &userInfo{OpenID: result.Data.OpenID, Name: result.Data.Name}, nil
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Check the HTTP status in the message; if 401/403, refresh or re-obtain the user_access_token before calling user_info.
  2. If 429, add throttling/backoff on user_info calls, especially in bulk enrichment loops.
  3. If 5xx, retry with backoff and check Lark service status.
  4. Verify network/proxy configuration if the status is a gateway error (502/504).

Example fix

// before
tok := getStoredUserToken()
info, err := enrichUserInfo(ctx, tok)

// after
info, err := enrichUserInfo(ctx, tok)
if err != nil && strings.Contains(err.Error(), "HTTP 401") {
    tok = refreshUserToken(ctx)
    info, err = enrichUserInfo(ctx, tok)
}
Defensive patterns

Strategy: try-catch

Validate before calling

func ensureUsableUserToken(tok string, fetchedAt time.Time) error {
    if tok == "" { return errors.New("missing user access token") }
    if time.Since(fetchedAt) > tokenTTL-2*time.Minute { return errors.New("user access token near expiry, refresh first") }
    return nil
}

Try / catch

info, err := enrichUserInfo(ctx, userToken)
if err != nil {
    var statusErr *HTTPStatusError
    switch {
    case strings.Contains(err.Error(), "HTTP 401"), strings.Contains(err.Error(), "HTTP 403"):
        userToken = refreshUserAccessToken(ctx)
        info, err = enrichUserInfo(ctx, userToken)
    case strings.Contains(err.Error(), "HTTP 429"):
        time.Sleep(rateLimitDelay); info, err = enrichUserInfo(ctx, userToken)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: resp.StatusCode != http.StatusOK on the user_info request: 401/403 from an invalid or expired user_access_token, 5xx from the service, 429 rate limiting, or proxy/gateway errors.

Common situations: User access token expired or revoked before the user_info call; tenant misconfig (wrong app credentials so token exchange returns an error page); Lark service incident; rate limiting after bulk user lookups.

Related errors


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