{"record":{"id":"5d23229103a82e12","repo":"sipeed/picoclaw","slug":"feishu-delete-api-error-code-d-msg-s","errorCode":null,"errorMessage":"feishu delete api error (code=%d msg=%s)","messagePattern":"feishu delete api error \\(code=(.+?) msg=(.+?)\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/feishu/feishu_64.go","lineNumber":288,"sourceCode":"\tdeleteFn := c.deleteMessageFn\n\tif deleteFn == nil {\n\t\tdeleteFn = c.deleteMessageAPI\n\t}\n\treturn deleteFn(ctx, chatID, messageID)\n}\n\nfunc (c *FeishuChannel) deleteMessageAPI(ctx context.Context, chatID, messageID string) error {\n\treq := larkim.NewDeleteMessageReqBuilder().\n\t\tMessageId(messageID).\n\t\tBuild()\n\n\tresp, err := c.client.Im.V1.Message.Delete(ctx, req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"feishu delete: %w\", err)\n\t}\n\tif !resp.Success() {\n\t\tc.invalidateTokenOnAuthError(resp.Code)\n\t\treturn fmt.Errorf(\"feishu delete api error (code=%d msg=%s)\", resp.Code, resp.Msg)\n\t}\n\treturn nil\n}\n\n// SendPlaceholder implements channels.PlaceholderCapable.\n// Sends an interactive card with placeholder text and returns its message ID.\nfunc (c *FeishuChannel) SendPlaceholder(ctx context.Context, chatID string) (string, error) {\n\tif !c.bc.Placeholder.Enabled {\n\t\tlogger.DebugCF(\"feishu\", \"Placeholder disabled, skipping\", map[string]any{\n\t\t\t\"chat_id\": chatID,\n\t\t})\n\t\treturn \"\", nil\n\t}\n\n\ttext := c.bc.Placeholder.GetRandomText()\n\n\tcardContent, err := buildMarkdownCard(text)\n\tif err != nil {","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/feishu/feishu_64.go#L270-L306","documentation":"FeishuChannel.deleteMessageAPI's Delete call parsed successfully but resp.Success() is false: Feishu returned a non-zero business code. Frequent codes: 230002 (bot lacks permission or is not in the chat) and invalid/nonexistent message_id (often because the message was already deleted, making this effectively idempotent-success). invalidateTokenOnAuthError(resp.Code) already ran for auth codes.","triggerScenarios":"Deleting a message that no longer exists (already deleted), deleting in a chat the bot left, missing im:message:delete scope, or an expired token (99991661/99991663).","commonSituations":"Placeholder already removed by an earlier retry (duplicate delete); user deleted the message first; bot permissions trimmed after initial setup; deleting across tenants with mismatched app credentials.","solutions":["Check the returned code: treat 'message not found' as success - the goal state (message gone) is achieved","230002: re-add the bot to the chat or grant im:message:delete scope in Feishu Open Platform","Auth codes: token invalidation already fired; verify app credentials if it persists","Deduplicate delete calls so retries do not re-delete"],"exampleFix":"// before\nif err := ch.DeleteMessage(ctx, chatID, msgID); err != nil {\n\treturn err // 'already deleted' surfaces as a failure\n}\n\n// after\nerr := ch.DeleteMessage(ctx, chatID, msgID)\nif err != nil && strings.Contains(err.Error(), \"feishu delete api error\") {\n\tlogger.Info(\"delete skipped\", \"msg_id\", msgID, \"err\", err) // treat as done\n\treturn nil\n}\nreturn err","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"var deleteCodeRe = regexp.MustCompile(`delete api error \\(code=(\\d+) msg=([^)]*)\\)`)\n\nfunc deleteFailureClass(err error) (code int, msg string, apiErr bool) {\n\tm := deleteCodeRe.FindStringSubmatch(err.Error())\n\tif m == nil { return 0, \"\", false }\n\tn, _ := strconv.Atoi(m[1])\n\treturn n, m[2], true\n}","tryCatchPattern":"err := ch.DeleteMessage(ctx, chatID, msgID)\nif code, msg, apiErr := deleteFailureClass(err); apiErr {\n\tswitch {\n\tcase code == 99991661 || code == 99991663: // auth: token refresh then retry once\n\tcase code == 230002 || strings.Contains(msg, \"not found\"): // already gone / no perm\n\t\treturn nil // goal state achieved or unreachable: accept\n\t}\n}\nreturn err","preventionTips":["Treat 'message not found' delete failures as success (idempotency)","Audit im:message:delete scope when wiring a new bot","Do not alert page on delete business errors - they are almost always benign","Keep auth-code handling centralized since invalidateTokenOnAuthError already fires"],"tags":["feishu","api-error","delete","permission","idempotency","lark-sdk"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}