#1259·whatsmeow

Status broadcasts block the handler queue for ~75s each; opt-in skip?

Author: moshedCreated Sep 7, 2026Updated Sep 20, 2026
Labelscan't reproduce

Problem

handleEncryptedMessage tries to decrypt every incoming status broadcast. On a companion device those messages are often undecryptable, so the attempt fails and a retry receipt follows. That path is slow, and handlerQueueLoop processes nodes in order, so every real message waits behind it.

A client that never reads other people's statuses still pays the full cost for each one.

Evidence

One bridge account, 18 days of logs, 18,123 lines. The account posts no statuses and receives about 80 a day.

measurement value
lines that are "node handling is taking long" / "took" warnings 10,960 (60% of the log)
"taking long" warnings whose node was status@broadcast 7,271 of 7,304 (99.5%)
"taking long" warnings from any other JID 33
nodes that took exactly 1m15s 3,582

The 1m15s figure is the striking part. It is not a spread — 3,582 nodes land on the same value, which points at a fixed timeout in the decrypt-and-retry path rather than at load.

The practical effect: an incoming message could sit for over a minute before the client stored it. On this account that also delayed an outgoing-message feature that reacts to the user's own text.

What helped, and what did not

Upgrading whatsmeow to the current version cut the warnings a long way, so recent work already improved this. It did not remove them.

Skipping status broadcasts before the decrypt attempt removed them completely. Measured over a 12-minute window after the change: 0 stalls over 5s, 0 over 30s.

Proposal

An opt-in client field, so nothing changes for callers who do want statuses:

go
// SkipStatusBroadcasts makes the client ack and discard incoming status
// broadcasts (JID status@broadcast) without attempting to decrypt them.
SkipStatusBroadcasts bool

Checked early in handleEncryptedMessage:

go
if cli.SkipStatusBroadcasts && info.Chat == types.StatusBroadcastJID {
    cli.Log.Debugf("Skipping status broadcast %s from %s", info.ID, info.Sender)
    cli.sendAck(ctx, node, 0)
    return
}

The node is still acked, so the server does not resend it. Outgoing statuses from the account are unaffected, and so are status nodes from newsletters.

Default is false, which keeps present behaviour.

I am happy to open a PR — the change is 21 lines across client.go and message.go, and has run in production for a day. Let me know if you would rather solve it another way, for example by deciding this inside the existing filtering rather than adding a field.

Environment

  • whatsmeow: current main as of 2026-09-06
  • Go 1.24, linux/arm64
  • Companion device (multi-device pairing), single account