Repository navigation
Description
Environment
- Evolution API v2.3.7 (Docker
evoapicloud/evolution-api:latest) - S3 storage enabled (Cloudflare R2, S3-compatible endpoint,
S3_SKIP_POLICY=true) - Channel: WhatsApp Baileys, multiple instances on the same server
- DB: PostgreSQL, Redis cache (
CACHE_REDIS_SAVE_INSTANCES=false)
Summary
Since enabling S3_ENABLED=true, media messages that reach an instance either (a) wrapped as a messageContextInfo-only payload, or (b) whose getBase64FromMediaMessage fails, no longer emit messages.upsert webhooks at all. The message row is still saved to the database, but the webhook is never fired. With S3 disabled, the same flows emit webhooks normally — the affected code path only runs when S3 is enabled.
Two user-visible scenarios (both reproduced):
- Document sent via API (
POST /message/sendMedia): the receiving instance receives the message as amessageContextInfo-only wrapper → guard returns → no S3 upload and no webhook. The receiver's integration never sees the message. - Media sent from the linked phone: the sending instance's own
fromMeecho →getBase64FromMediaMessagefails → no upload and no webhook. The sender's integration never registers the outgoing media (onlymessages.updatereceipts arrive).
Evidence (v2.3.7 logs, timestamps local)
Scenario 1 — document sent via API from instance A to instance B (B's webhook received nothing):
[instanceB] VERBOSE [ChannelStartupService] Message contains only messageContextInfo, skipping media processing
[instanceB] VERBOSE [ChannelStartupService] No valid media to upload (messageContextInfo only), skipping MinIO
- No S3 object created under instance B's prefix.
- No
p.sendData-Webhookblock withevent: 'messages.upsert'in the logs for B — onlymessages.updatereceipts from A.
Scenario 2 — photo sent from A's linked phone (A's webhook never got the fromMe upsert):
[instanceA] VERBOSE [ChannelStartupService] No valid media to upload (messageContextInfo only), skipping MinIO
- The receiving instance processed the same message fine (S3 object uploaded + webhook delivered).
- Instance A emitted only
messages.update(SERVER_ACK / DELIVERY_ACK) — nomessages.upsert.
Relevant code (v2.3.7, whatsapp.baileys.service.js)
n.message = n.message[d].message;
if ("messageContextInfo" in n.message && Object.keys(n.message).length === 1)
return this.logger.verbose("Message contains only messageContextInfo, skipping media processing"), null;
...
const D = await this.getBase64FromMediaMessage({ message: b }, true);
if (!D) {
this.logger.verbose("No valid media to upload (messageContextInfo only), skipping MinIO");
return; // ← aborts the rest of the message processing, including webhook emission
}The guard introduced by the 2.3.7 fix "MinIO Upload: Handle messageContextInfo in media upload to prevent MinIO errors" appears to early-return out of the whole upsert flow instead of skipping only the media upload. Because this code path only executes when S3 is enabled, enabling S3 silently drops messages.upsert webhooks for these messages.
Expected behavior
Skip (or defer) the S3 upload but still emit messages.upsert — e.g. without mediaUrl, or with the original message payload — so integrations keep receiving every message. Ideally, retry the media upload when the real payload arrives after decryption.
Steps to reproduce
- Two instances (different WhatsApp numbers), webhooks enabled for both, S3 enabled.
POST /message/sendMedia(document via URL) from instance A to B's number → B's webhook receives nothing for the document (it arrives on the phone and is saved to B's DB).- Send a photo from A's linked phone to B → A's webhook receives no
fromMeupsert for the photo; B receives it normally. - Disable S3 and repeat → webhooks flow again.
Workaround
None besides disabling S3 (restores the webhook flow) or synthesizing outgoing messages on the integration side from the API send response plus messages.update receipts.
Happy to provide more log excerpts or a live reproduction if needed. Thanks for the great project!
Activity
cnemer21 commentedon Oct 26, 2026
Update from the reporter — we confirmed the root cause in production and worked around it with a local patch. Sharing the findings + fix in case it helps land an official fix.
Confirmation of root cause
The fatal early-
returnexists in two code paths (both insidedist's bundled Baileys service):messages.upserthandler (the main one): DB save happens first (message.create), then the S3 block runs, then the webhook is emitted (sendDataWebhook("messages.upsert", ...)). WhengetBase64FromMediaMessagereturns falsy (wrapper-only message or download failure), thereturnskips the webhook entirely. Message is in the DB but integrations never see it.sendMessageWithTyping, emitssend.message): same pattern, samereturn.Additional detail that matters for anyone patching: in the published image, the executing code is the single bundle
dist/main.js(entry:node dist/main). The files underdist/api/**are not loaded at runtime — patching those chunks has no effect.Suggested fix (what we applied, minified equivalent shown)
Make a failed media extraction skip only the upload, never the rest of the flow:
Resulting behavior (validated in production, 2 instances, multiple media types):
fromMeechomessages.upsertmediaUrl)messages.upsertdocumentMessage/imageMessagepayload (nomediaUrlwhen extraction fails)mediaUrl(upload works)No duplicate or empty-payload webhooks were observed: the guard only triggers for messages that already passed
hasValidMediaContent, so the emitted payload always contains the real message content; wrapper-only protocol events keep their previous behavior (they never reach this path).Deployment note (local workaround until official fix)
We ship the patched
main.jsvia a read-only bind mount in docker-compose, with the base image pinned by digest, plus the original file kept alongside for rollback. Happy to test an official fix when available — happy to provide more evidence if useful.abitech-dev commentedon Oct 26, 2026
Good