{"record":{"id":"1c9fe9920a5556e0","repo":"larksuite/cli","slug":"fetch-bot-info-http-d","errorCode":null,"errorMessage":"fetch bot info: HTTP %d","messagePattern":"fetch bot info: HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/common/runner.go","lineNumber":171,"sourceCode":"\t\treturn nil, fmt.Errorf(\"BotInfo not available (runtime context not fully initialized)\")\n\t}\n\treturn ctx.botInfoFunc()\n}\n\n// fetchBotInfo calls /bot/v3/info using bot identity and parses the response.\nfunc (ctx *RuntimeContext) fetchBotInfo() (*BotInfo, error) {\n\tif !ctx.Config.CanBot() {\n\t\treturn nil, fmt.Errorf(\"fetch bot info: bot identity is not available in current credential context\")\n\t}\n\tresp, err := ctx.DoAPIAsBot(&larkcore.ApiReq{\n\t\tHttpMethod: http.MethodGet,\n\t\tApiPath:    \"/open-apis/bot/v3/info\",\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fetch bot info: %w\", err)\n\t}\n\tif resp.StatusCode >= 400 {\n\t\treturn nil, fmt.Errorf(\"fetch bot info: HTTP %d\", resp.StatusCode)\n\t}\n\t// /open-apis/bot/v3/info returns `{code, msg, bot: {...}}` — the bot\n\t// payload is under \"bot\", not \"data\" as the newer Lark API convention.\n\tvar envelope 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\tAppName string `json:\"app_name\"`\n\t\t} `json:\"bot\"`\n\t}\n\tif err := json.Unmarshal(resp.RawBody, &envelope); err != nil {\n\t\treturn nil, fmt.Errorf(\"fetch bot info: unmarshal: %w\", err)\n\t}\n\tif envelope.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"fetch bot info: [%d] %s\", envelope.Code, envelope.Msg)\n\t}\n\tif envelope.Data.OpenID == \"\" {","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/common/runner.go#L153-L189","documentation":"The bot info endpoint returned an HTTP status >= 400, so the response is not usable. The library reports the status code without parsing a body, because at this layer the status alone indicates the API rejected the request (auth, permissions, or gateway error).","triggerScenarios":"DoAPIAsBot succeeded at transport level but /bot/v3/info replied 4xx/5xx — e.g. 401/403 from missing bot scopes or invalid bot token, 404 from wrong API base/gateway, 5xx from server-side issues.","commonSituations":"App missing the bot capability or required scopes for /bot/v3/info; stale token after credential rotation; misconfigured gateway/base-URL override pointing at the wrong environment.","solutions":["Check the HTTP status in the message: 401/403 → fix bot credentials/scopes; 404 → verify API base URL; 5xx → retry later","Grant the app the scopes required for /open-apis/bot/v3/info and enable bot capability","Re-run after refreshing credentials; if persistent, capture the raw response with debug logging to see the error body"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"info, err := ctx.BotInfo()\nif err != nil {\n\tvar httpErr struct{ Status int }\n\tif m := regexp.MustCompile(`HTTP (\\d+)`).FindStringSubmatch(err.Error()); m != nil {\n\t\tswitch m[1] {\n\t\tcase \"401\", \"403\": /* fix scopes/credentials */\n\t\tcase \"404\": /* check base URL */\n\t\tdefault: /* retry later for 5xx */\n\t\t}\n\t}\n\treturn err\n}","preventionTips":["Grant the app all scopes required by /open-apis/bot/v3/info","Keep the API base URL/gateway configuration pointing at the correct environment","Monitor for 5xx and add bounded retry with backoff for transient server errors"],"tags":["go","http","api-error","bot-identity","permissions"],"backgroundTag":"http-4xx-api-error","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"}