fix: prevent media processing failures from aborting webhook delivery#266
fix: prevent media processing failures from aborting webhook delivery#266dumilani wants to merge 1 commit intoasternic:mainfrom
Conversation
When skipMedia is false, each media processing block (image, audio, document, video, sticker) contains multiple return statements that abort the entire event handler if any step fails (e.g., directory creation, media download, file write, base64 conversion). This means that if media processing fails for any reason, the webhook is never sent to the consumer, causing the message to silently disappear. The consumer never knows the message existed. This is a critical issue because many consumers handle media downloads independently via the /chat/downloadimage (and similar) API endpoints using the encryption keys provided in the message payload. They do not depend on the pre-processed media from the webhook, so aborting the webhook is unnecessary and harmful. The fix wraps each media processing block in an anonymous function (closure) so that return statements only exit the closure, not the parent event handler. This ensures the webhook is always delivered regardless of media processing outcome. Affected media types: image, audio, document, video, sticker.
Summary of ChangesHello @dumilani, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request resolves a critical issue where transient failures during media processing could lead to silent message loss by prematurely aborting webhook delivery. The changes ensure that all webhooks are consistently delivered to consumers, irrespective of media processing outcomes, significantly enhancing message reliability and preventing data loss. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
The pull request correctly addresses the issue where media processing failures would abort the entire event handler, preventing webhook delivery. By wrapping each media processing block in an anonymous closure, return statements now only exit the local scope, allowing the handler to proceed and send the webhook. This is a significant improvement for reliability. However, the implementation highlights some existing vulnerabilities within these blocks, such as potential panics due to unchecked slice access and temporary file leaks when errors occur. Addressing these would further enhance the robustness of the media handling logic.
| // try to get Image if any | ||
| img := evt.Message.GetImageMessage() | ||
| if img != nil { | ||
| func() { |
There was a problem hiding this comment.
While wrapping this block in a closure prevents the main event handler from aborting on error, the implementation inside (specifically line 860) is susceptible to a panic if mime.ExtensionsByType returns an empty slice (which happens for unknown or empty MIME types). Additionally, any early return within this closure (e.g., at line 901) will bypass the os.Remove(tmpPath) call at line 914, leading to temporary file leaks in /tmp. It is recommended to use defer os.Remove(tmpPath) once the path is determined to ensure cleanup regardless of how the closure exits.
| // try to get Video if any | ||
| video := evt.Message.GetVideoMessage() | ||
| if video != nil { | ||
| func() { |
There was a problem hiding this comment.
| // try to get Audio if any | ||
| audio := evt.Message.GetAudioMessage() | ||
| if audio != nil { | ||
| func() { |
| // try to get Document if any | ||
| document := evt.Message.GetDocumentMessage() | ||
| if document != nil { | ||
| func() { |
|
|
||
| sticker := evt.Message.GetStickerMessage() | ||
| if sticker != nil { | ||
| func() { |
Problem
When
skipMediaisfalse(the default), theevents.Messagehandler inwmiau.goattempts to download and process media (image, audio, document, video, sticker) before sending the webhook to the consumer.Each media processing block contains multiple
returnstatements that abort the entire event handler if any step fails — directory creation, media download, file write, or base64 conversion. When this happens, the webhook is never sent, and the message silently disappears from the consumer's perspective.Example (image block, same pattern in all 5 media types):
Impact
/chat/downloadimage,/chat/downloadvideo,/chat/downloaddocument, and/chat/downloadaudioAPI endpoints using the encryption keys from the message payload — they don't need the pre-processed media from the webhook at allAffected media types
All five: image, audio, document, video, sticker.
Solution
Wrap each media processing block's body in an anonymous function (closure), so that
returnstatements only exit the closure — not the parent event handler:This ensures the webhook is always delivered to the consumer, regardless of whether media processing succeeded or failed. Media processing errors are still logged, and the
--skipmediaflag continues to work as before.Changes
wmiau.go: Wrapped the body of each media processing block (image, audio, document, video, sticker) infunc() { ... }()closuresTesting
--skipmediaflag behavior is unchangedpostmapfields correctly (base64, s3, etc.)