{"record":{"id":"13059a32e71545f2","repo":"babalae/better-genshin-impact","slug":"feishu-upload-image-not-found-image-key","errorCode":null,"errorMessage":"Feishu upload image not found image key","messagePattern":"Feishu upload image not found image key","errorType":"exception","errorClass":"NotifierException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/Service/Notifier/FeishuNotifier.cs","lineNumber":190,"sourceCode":"                throw new NotifierException($\"Feishu upload image failed with code: {uploadImageResponse.StatusCode}\");\n            }\n            using (JsonDocument doc = JsonDocument.Parse(uploadResponseString))\n            {\n                JsonElement root = doc.RootElement;\n                if (root.TryGetProperty(\"data\", out JsonElement dataElement) &&\n                    dataElement.TryGetProperty(\"image_key\", out JsonElement imageKeyElement))\n                {\n                    var keyNullable = imageKeyElement.GetString();\n                    if (keyNullable != null)\n                    {\n                        imageKey = keyNullable;\n                    }\n                }\n            }\n        }\n        if (string.IsNullOrEmpty(imageKey))\n        {\n            throw new NotifierException($\"Feishu upload image not found image key\");\n        }\n        return imageKey;\n    }\n}\n","sourceCodeStart":172,"sourceCodeEnd":195,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/Service/Notifier/FeishuNotifier.cs#L172-L195","documentation":"Thrown by FeishuNotifier.UploadImage after a successful (2xx) HTTP POST to the Feishu image-upload endpoint (https://open.feishu.cn/open-apis/im/v1/images) when the parsed JSON body does not contain a non-empty data.image_key. The library treats a missing/null/empty image_key as a hard failure because the subsequent 'post' message (tag=img) needs that key to reference the uploaded asset.","triggerScenarios":"uploadImageResponse.IsSuccessStatusCode is true, JsonDocument.Parse succeeds, but root.TryGetProperty(\"data\", ...) or data.TryGetProperty(\"image_key\", ...) returns false, OR imageKeyElement.GetString() returns null, leaving imageKey empty and triggering the line-188 IsNullOrEmpty guard.","commonSituations":"Feishu app lacks the im:resource permission scope; the access token is valid but the app is not approved/published so the upload silently returns code!=0 with no data node; the PNG payload exceeds Feishu size limits and the API returns a 200 with an error body; AppId/AppSecret mismatch produces a token with no upload rights; API contract drift after a Feishu OpenAPI version bump.","solutions":["Capture and log uploadResponseString before the throw to see the actual Feishu error code/message (e.g. code 99991663 permission denied).","Confirm the Feishu app has the im:resource (im:message:send_as_image) permission and is published/approved in the developer console.","Verify AppId/AppSecret are correct and that GetAccessToken returns a tenant_access_token for the same app used for upload.","If the response shape changed, harden the parser to read data.code / data.msg and surface them in the exception instead of a generic 'not found image key'."],"exampleFix":"// before\nif (string.IsNullOrEmpty(imageKey))\n{\n    throw new NotifierException($\"Feishu upload image not found image key\");\n}\n\n// after\nvar code = root.TryGetProperty(\"code\", out var c) ? c.GetInt32() : -1;\nvar msg = root.TryGetProperty(\"msg\", out var m) ? m.GetString() : uploadResponseString;\nif (string.IsNullOrEmpty(imageKey))\n{\n    throw new NotifierException($\"Feishu upload image returned no image_key (code={code}, msg={msg})\");\n}","handlingStrategy":"try-catch","validationCode":"// Before enabling the Feishu image path, sanity-check the app credentials are present.\nif (notificationData.Screenshot != null\n    && (string.IsNullOrEmpty(notifier.AppId) || string.IsNullOrEmpty(notifier.AppSecret)))\n{\n    // skip image upload, fall back to text-only — or warn the user.\n}","typeGuard":"// Ensure the credentials look like Feishu app credentials before sending.\nstatic bool HasFeishuAppCredentials(FeishuNotifier n)\n    => !string.IsNullOrEmpty(n.AppId) && !string.IsNullOrEmpty(n.AppSecret)\n       && n.AppId.StartsWith(\"cli_\");","tryCatchPattern":"try { await feishuNotifier.SendAsync(data); }\ncatch (NotifierException ex) when (ex.Message.Contains(\"image key\"))\n{\n    // degrade to text-only and retry once\n    data.Screenshot = null;\n    await feishuNotifier.SendAsync(data);\n}","preventionTips":["Publish and approve the Feishu app with im:resource permission before enabling image notifications.","Log the raw Feishu response body when parsing fails so the real error code surfaces.","Fall back to text-only when image upload fails rather than dropping the whole notification."],"tags":["feishu","api-contract","image-upload","notifier","config"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}