{"record":{"id":"91a28357f31b672c","repo":"larksuite/cli","slug":"user-info-api-returned-http-d","errorCode":null,"errorMessage":"user_info API returned HTTP %d","messagePattern":"user_info API returned HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/credential/user_info.go","lineNumber":38,"sourceCode":"// fetchUserInfo calls /open-apis/authen/v1/user_info with a UAT to get the user's identity.\nfunc fetchUserInfo(ctx context.Context, httpClient *http.Client, brand core.LarkBrand, uat string) (*userInfo, error) {\n\tep := core.ResolveEndpoints(brand)\n\turl := ep.Open + \"/open-apis/authen/v1/user_info\"\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+uat)\n\n\tresp, err := httpClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"user_info API returned HTTP %d\", resp.StatusCode)\n\t}\n\n\tvar result struct {\n\t\tCode int    `json:\"code\"`\n\t\tMsg  string `json:\"msg\"`\n\t\tData struct {\n\t\t\tOpenID string `json:\"open_id\"`\n\t\t\tName   string `json:\"name\"`\n\t\t} `json:\"data\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn nil, err\n\t}\n\tif result.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"user_info API error: [%d] %s\", result.Code, result.Msg)\n\t}\n\treturn &userInfo{OpenID: result.Data.OpenID, Name: result.Data.Name}, nil\n}","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/credential/user_info.go#L20-L56","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the HTTP status in the message; if 401/403, refresh or re-obtain the user_access_token before calling user_info.","If 429, add throttling/backoff on user_info calls, especially in bulk enrichment loops.","If 5xx, retry with backoff and check Lark service status.","Verify network/proxy configuration if the status is a gateway error (502/504)."],"exampleFix":"// before\ntok := getStoredUserToken()\ninfo, err := enrichUserInfo(ctx, tok)\n\n// after\ninfo, err := enrichUserInfo(ctx, tok)\nif err != nil && strings.Contains(err.Error(), \"HTTP 401\") {\n    tok = refreshUserToken(ctx)\n    info, err = enrichUserInfo(ctx, tok)\n}","handlingStrategy":"try-catch","validationCode":"func ensureUsableUserToken(tok string, fetchedAt time.Time) error {\n    if tok == \"\" { return errors.New(\"missing user access token\") }\n    if time.Since(fetchedAt) > tokenTTL-2*time.Minute { return errors.New(\"user access token near expiry, refresh first\") }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"info, err := enrichUserInfo(ctx, userToken)\nif err != nil {\n    var statusErr *HTTPStatusError\n    switch {\n    case strings.Contains(err.Error(), \"HTTP 401\"), strings.Contains(err.Error(), \"HTTP 403\"):\n        userToken = refreshUserAccessToken(ctx)\n        info, err = enrichUserInfo(ctx, userToken)\n    case strings.Contains(err.Error(), \"HTTP 429\"):\n        time.Sleep(rateLimitDelay); info, err = enrichUserInfo(ctx, userToken)\n    }\n    if err != nil { return err }\n}","preventionTips":["Refresh the user_access_token proactively before it expires rather than on failure.","Throttle bulk user_info enrichment to avoid 429s.","Distinguish HTTP-status failures from API-code failures by inspecting the message before retrying.","Check Lark service status before paging on 5xx-driven occurrences."],"tags":["http","user-info","auth","status-code"],"backgroundTag":"unexpected-http-status","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}