{"record":{"id":"ece42bd82f089a2c","repo":"sipeed/picoclaw","slug":"feishu-image-send-api-error-code-d-msg-s","errorCode":null,"errorMessage":"feishu image send api error (code=%d msg=%s)","messagePattern":"feishu image send api error \\(code=(.+?) msg=(.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/feishu/feishu_64.go","lineNumber":1178,"sourceCode":"\n\t// Send image message\n\tcontent, _ := json.Marshal(map[string]string{\"image_key\": imageKey})\n\treq := larkim.NewCreateMessageReqBuilder().\n\t\tReceiveIdType(larkim.CreateMessageV1ReceiveIDTypeChatId).\n\t\tBody(larkim.NewCreateMessageReqBodyBuilder().\n\t\t\tReceiveId(chatID).\n\t\t\tMsgType(larkim.MsgTypeImage).\n\t\t\tContent(string(content)).\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 image send: %w\", err)\n\t}\n\tif !resp.Success() {\n\t\tc.invalidateTokenOnAuthError(resp.Code)\n\t\treturn fmt.Errorf(\"feishu image send api error (code=%d msg=%s)\", resp.Code, resp.Msg)\n\t}\n\treturn nil\n}\n\n// sendFile uploads a file and sends it as a message.\nfunc (c *FeishuChannel) sendFile(ctx context.Context, chatID string, file *os.File, filename, fileType string) error {\n\t// Map part type to Feishu file type\n\tfeishuFileType := \"stream\"\n\tswitch fileType {\n\tcase \"audio\":\n\t\tfeishuFileType = \"opus\"\n\tcase \"video\":\n\t\tfeishuFileType = \"mp4\"\n\t}\n\n\t// Upload file to get file_key\n\tuploadReq := larkim.NewCreateFileReqBuilder().\n\t\tBody(larkim.NewCreateFileReqBodyBuilder().","sourceCodeStart":1160,"sourceCodeEnd":1196,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/feishu/feishu_64.go#L1160-L1196","documentation":"Feishu rejected the image message send with a non-zero business code after the upload succeeded. invalidateTokenOnAuthError runs first, so a 99991663 clears the token cache. The dominant code is 230002 invalid image_key — keys are app-scoped and short-lived.","triggerScenarios":"Message.Create with msg_type=image fails: image_key expired because too much time passed between upload and send; key was minted by a different app_id; bot lacks permission to the target chat; 99991663 stale token.","commonSituations":"Retries after backoff that outlive the key's validity; uploading with one credential and sending with another after a config change; bot removed from the chat between upload and send.","solutions":["Treat upload+send as one unit: on this error, re-upload to get a fresh image_key and immediately send.","Confirm upload and send use the same app credentials (image_key is bound to the app).","Verify the bot is still in the target chat and has im:message scope.","If code is 99991663, the token cache was already cleared — the next attempt fetches a fresh token."],"exampleFix":"// before — upload once, send; a backoff-delayed retry can outlive the image_key\nkey := upload(ctx, f)\nerr := sendImageMsg(ctx, chatID, key)\n\n// after — re-upload on invalid-key so retry always uses a fresh key\nkey := upload(ctx, f)\nerr := sendImageMsg(ctx, chatID, key)\nif err != nil && strings.Contains(err.Error(), \"code=230002\") {\n    key = upload(ctx, f)\n    err = sendImageMsg(ctx, chatID, key)\n}","handlingStrategy":"retry","validationCode":"// minimize key age: upload immediately before send\n// (there is no API to validate an image_key without spending it)","typeGuard":"func isInvalidImageKey(err error) bool {\n    return strings.Contains(err.Error(), \"feishu image send api error\") && strings.Contains(err.Error(), \"code=230002\")\n}","tryCatchPattern":"if err := sendImageFlow(ctx, chatID, f); err != nil {\n    if isInvalidImageKey(err) {\n        // key expired/app-mismatch: re-upload and resend immediately\n        err = sendImageFlow(ctx, chatID, f)\n    }\n    return err\n}","preventionTips":["Never cache image_keys across requests — mint and use immediately","Use one app credential for upload and send","Keep bot membership current in target chats"],"tags":["feishu","send","go","lark-api","upload"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}