{"record":{"id":"0dad970c40c6cf05","repo":"sipeed/picoclaw","slug":"feishu-api-error-code-d-msg-s-w","errorCode":null,"errorMessage":"feishu api error (code=%d msg=%s): %w","messagePattern":"feishu api error \\(code=(.+?) msg=(.+?)\\): %w","errorType":"exception","errorClass":"ErrTemporary","httpStatus":null,"severity":"error","filePath":"pkg/channels/feishu/feishu_64.go","lineNumber":1092,"sourceCode":"// sendCard sends an interactive card message to a chat.\nfunc (c *FeishuChannel) sendCard(ctx context.Context, chatID, cardContent string) (string, error) {\n\treq := larkim.NewCreateMessageReqBuilder().\n\t\tReceiveIdType(larkim.CreateMessageV1ReceiveIDTypeChatId).\n\t\tBody(larkim.NewCreateMessageReqBodyBuilder().\n\t\t\tReceiveId(chatID).\n\t\t\tMsgType(larkim.MsgTypeInteractive).\n\t\t\tContent(cardContent).\n\t\t\tBuild()).\n\t\tBuild()\n\n\tresp, err := c.client.Im.V1.Message.Create(ctx, req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"feishu send card: %w\", channels.ErrTemporary)\n\t}\n\n\tif !resp.Success() {\n\t\tc.invalidateTokenOnAuthError(resp.Code)\n\t\treturn \"\", fmt.Errorf(\"feishu api error (code=%d msg=%s): %w\", resp.Code, resp.Msg, channels.ErrTemporary)\n\t}\n\n\tlogger.DebugCF(\"feishu\", \"Feishu card message sent\", map[string]any{\n\t\t\"chat_id\": chatID,\n\t})\n\n\tif resp.Data != nil && resp.Data.MessageId != nil {\n\t\treturn *resp.Data.MessageId, nil\n\t}\n\treturn \"\", nil\n}\n\n// sendText sends a plain text message to a chat (fallback when card fails).\nfunc (c *FeishuChannel) sendText(ctx context.Context, chatID, text string) (string, error) {\n\tcontent, _ := json.Marshal(map[string]string{\"text\": text})\n\n\treq := larkim.NewCreateMessageReqBuilder().\n\t\tReceiveIdType(larkim.CreateMessageV1ReceiveIDTypeChatId).","sourceCodeStart":1074,"sourceCodeEnd":1110,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/feishu/feishu_64.go#L1074-L1110","documentation":"Feishu (Lark) Open API rejected an interactive card message: the HTTP call completed but resp.Success() is false, so the body carried a non-zero business code (resp.Code/resp.Msg). The channel wraps channels.ErrTemporary, so the outbound queue retries with exponential backoff (500ms*2^attempt, max 8s). Before returning it calls invalidateTokenOnAuthError, which clears the cached tenant_access_token when code is 99991663 (see pkg/channels/feishu/feishu_64.go:1261).","triggerScenarios":"Im.V1.Message.Create with msg_type=interactive fails: bot not a member of the target chat or missing im:message permission (230001), stale/invalid tenant_access_token (99991663), QPS/rate limit exceeded (99991400), non-existent chat_id (receive_id invalid), or card JSON that does not match the card schema.","commonSituations":"Bot was removed from the group; app_id/app_secret rotated so the cached token is stale; card schema change after Feishu deprecates elements; burst sends tripping tenant rate limits.","solutions":["Read the code=%d in the message text: 99991663 -> fix app credentials (token cache is auto-cleared, next attempt fetches a fresh token); 230001 -> add the bot to the target chat and grant im:message scope; 99991400 -> throttle card sends.","Verify chat_id is the oc_-prefixed chat ID taken from the inbound event, not an open_id/user ID.","Validate the card JSON against the Feishu card builder (element types, version field) before sending.","If it persists, copy msg= verbatim into the Feishu open-platform error-code lookup."],"exampleFix":"// before\n_, err := ch.Send(ctx, msg) // error surfaces as 'feishu api error (code=230001 ...)'\n\n// after: classify and act on the wrapped sentinel\nif _, err := ch.Send(ctx, msg); err != nil {\n    if errors.Is(err, channels.ErrTemporary) {\n        // queue will retry with backoff; inspect text for code= to find root cause\n        if strings.Contains(err.Error(), \"code=230001\") {\n            // permanent root cause: stop retrying, add bot to chat\n        }\n    }\n}","handlingStrategy":"retry","validationCode":"if msg.ChatID == \"\" || !strings.HasPrefix(msg.ChatID, \"oc_\") {\n    return fmt.Errorf(\"invalid feishu chat_id %q: expected oc_-prefixed chat id\", msg.ChatID)\n}\nif _, jerr := json.Marshal(card); jerr != nil {\n    return fmt.Errorf(\"card content not serializable: %w\", jerr)\n}","typeGuard":"func isTemporarySendErr(err error) bool {\n    return errors.Is(err, channels.ErrTemporary)\n}","tryCatchPattern":"if _, err := ch.Send(ctx, msg); err != nil {\n    switch {\n    case errors.Is(err, channels.ErrTemporary):\n        // scheduler retries with backoff; parse 'code=' in text for 99991663/230001/99991400\n    default:\n        // permanent — do not retry\n    }\n}","preventionTips":["Keep the bot a member of every target chat","Grant im:message scope before first send","Rate-limit card sends below the tenant QPS cap","Alert when code=99991663 appears more than once — credential rotation may be needed"],"tags":["feishu","lark-api","go","card-message","retry"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}