{"record":{"id":"bc474e5785d7e666","repo":"larksuite/cli","slug":"open-id-is-empty","errorCode":null,"errorMessage":"open_id is empty","messagePattern":"open_id is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/identitydiag/diagnostics.go","lineNumber":433,"sourceCode":"\tparseErr := json.Unmarshal(body, &envelope)\n\n\tif resp.StatusCode >= 400 {\n\t\t// Lark error responses are usually `{code, msg}` envelopes even on\n\t\t// non-2xx — surface them when present so callers see why bot auth\n\t\t// was rejected, not just the bare HTTP code.\n\t\tif parseErr == nil && envelope.Code != 0 {\n\t\t\treturn nil, fmt.Errorf(\"HTTP %d: [%d] %s\", resp.StatusCode, envelope.Code, envelope.Msg)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"HTTP %d\", resp.StatusCode)\n\t}\n\tif parseErr != nil {\n\t\treturn nil, fmt.Errorf(\"parse response: %w\", parseErr)\n\t}\n\tif envelope.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"[%d] %s\", envelope.Code, envelope.Msg)\n\t}\n\tif envelope.Data.OpenID == \"\" {\n\t\treturn nil, errors.New(\"open_id is empty\")\n\t}\n\treturn &botInfo{OpenID: envelope.Data.OpenID, AppName: envelope.Data.AppName}, nil\n}\n\nfunc fillTokenFields(id *Identity, token *larkauth.StoredUAToken) {\n\tid.TokenStatus = larkauth.TokenStatus(token)\n\tid.Scope = token.Scope\n\tid.ExpiresAt = formatMillis(token.ExpiresAt)\n\tid.RefreshExpiresAt = formatMillis(token.RefreshExpiresAt)\n\tid.GrantedAt = formatMillis(token.GrantedAt)\n}\n\nfunc formatMillis(ms int64) string {\n\tif ms <= 0 {\n\t\treturn \"\"\n\t}\n\treturn time.UnixMilli(ms).Format(time.RFC3339)\n}","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/identitydiag/diagnostics.go#L415-L451","documentation":"fetchBotInfo validates the bot-info API envelope and fails when Data.OpenID is empty even though the API reported success (Code==0). This is a defensive invariant check: a bot identity diagnosis is meaningless without an open_id, so the library treats a success envelope lacking one as an error rather than returning a hollow botInfo. It propagates to diagnoseBot/diagnoseExternalBot.","triggerScenarios":"Calling diagnoseBot/diagnoseExternalBot where the bot-info endpoint returns Code==0 but Data.OpenID is \"\" — e.g. an API behavior change, an unexpected app type, or a stubbed/proxied response missing fields.","commonSituations":"Feishu/Lark API version change altering the response shape; a gateway or mock returning an empty payload with code 0; diagnosing an app type that has no bot open_id.","solutions":["Upgrade lark-cli to the latest version in case the response schema changed","Check the app actually has bot capability enabled in the developer console","Inspect the raw API response (log_id / gateway) to confirm whether the server truly returned an empty open_id"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if envelope.Code == 0 && envelope.Data.OpenID == \"\" {\n    // treat as suspect response before consuming\n}","typeGuard":"func botInfoValid(b *botInfo) bool { return b != nil && b.OpenID != \"\" }","tryCatchPattern":"info, err := fetchBotInfo(ctx, client)\nif err != nil {\n    // includes 'open_id is empty' — retry once, then surface diagnostics\n}","preventionTips":["Pin/test against current bot-info API versions","Validate bot capability is enabled before diagnosing","Log raw envelopes to detect shape drift early","Fail fast on success envelopes with empty required fields"],"tags":["api","identity","diagnostics"],"backgroundTag":"empty-api-response-field","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"}