{"record":{"id":"24d8f8e6f4398e18","repo":"larksuite/cli","slug":"fetch-bot-info-open-id-is-empty","errorCode":null,"errorMessage":"fetch bot info: open_id is empty","messagePattern":"fetch bot info: open_id is empty","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/common/runner.go","lineNumber":190,"sourceCode":"\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 == \"\" {\n\t\treturn nil, fmt.Errorf(\"fetch bot info: open_id is empty\")\n\t}\n\treturn &BotInfo{OpenID: envelope.Data.OpenID, AppName: envelope.Data.AppName}, nil\n}\n\n// Ctx returns the context.Context propagated from cmd.Context().\nfunc (ctx *RuntimeContext) Ctx() context.Context { return ctx.ctx }\n\n// getAPIClient returns the cached APIClient, creating it on first use.\n// Thread-safe via sync.OnceValues (initialized in newRuntimeContext).\n// Falls back to direct construction for test contexts that bypass newRuntimeContext.\nfunc (ctx *RuntimeContext) getAPIClient() (*client.APIClient, error) {\n\tif ctx.offline {\n\t\treturn nil, errs.NewValidationError(errs.SubtypeFailedPrecondition, \"OpenAPI requests are unavailable during dry-run\")\n\t}\n\tif ctx.apiClientFunc != nil {\n\t\treturn ctx.apiClientFunc()\n\t}\n\treturn ctx.Factory.NewAPIClientWithConfig(ctx.Config)","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/common/runner.go#L172-L208","documentation":"The bot info response succeeded (code 0) but the bot object has an empty open_id, so no usable bot identity exists. The library treats bot identity as mandatory output of this endpoint; an empty open_id implies a malformed or semantically empty bot record.","triggerScenarios":"envelope.Data.OpenID == \"\" after successful decode — e.g. app has no bot bound, the tenant stripped identity fields, or the endpoint returned an empty bot object.","commonSituations":"App created without adding the bot capability, so /bot/v3/info returns an empty bot; region/tenant configurations that omit open_id.","solutions":["Enable the bot capability for the app and confirm a bot instance exists in the tenant, then retry","If the endpoint legitimately returns no bot, stop relying on bot identity: pass the identity explicitly or use user-based identity flows","File/report a schema issue if the API returns code 0 with an empty bot object unexpectedly"],"exampleFix":"// before\ninfo, _ := ctx.BotInfo()\nuseId(info.OpenID) // \"open_id is empty\" upstream\n// after\ninfo, err := ctx.BotInfo()\nif err != nil || info.OpenID == \"\" { return errs.NewValidationError(errs.SubtypeFailedPrecondition, \"bot identity unavailable; pass --attendee explicitly\") }","handlingStrategy":"validation","validationCode":"info, err := ctx.BotInfo()\nif err == nil && info.OpenID == \"\" {\n\t// treat as unavailable identity; require explicit ID\n}","typeGuard":"func hasBotIdentity(info *common.BotInfo) bool { return info != nil && info.OpenID != \"\" }","tryCatchPattern":"info, err := ctx.BotInfo()\nif err != nil || info == nil || info.OpenID == \"\" {\n\treturn errs.NewValidationError(errs.SubtypeFailedPrecondition, \"bot identity unavailable; provide the attendee/identity explicitly\")\n}","preventionTips":["Ensure the app has the bot capability enabled so /bot/v3/info returns a real bot record","Always nil/empty-check BotInfo.OpenID before using it as an ID","Offer an explicit identity flag as a fallback in commands that need an ID"],"tags":["go","bot-identity","empty-response","api-schema"],"backgroundTag":"bot-identity-missing","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"}