Skip to content

fix(CannedMessage): keep listening after a relay ACK - #11479

Draft
h3lix1 wants to merge 2 commits into
meshtastic:developfrom
h3lix1:fix/cannedmessage-relay-ack-latch
Draft

fix(CannedMessage): keep listening after a relay ACK#11479
h3lix1 wants to merge 2 commits into
meshtastic:developfrom
h3lix1:fix/cannedmessage-relay-ack-latch

Conversation

@h3lix1

@h3lix1 h3lix1 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

CannedMessageModule::handleReceived() is guarded by waitingForAck, and the relay-ACK branch
clears it. So the first node that forwards our DM ends our interest in the exchange. The
destination's own ACK arrives later, fails the guard at the top of the function, and is dropped.

The message shows RELAYED permanently even though it was delivered, and this->ack,
this->incoming and the stored AckStatus all stay at the relay result.

The fix leaves waitingForAck set on a relay ACK. A relay ACK says someone forwarded the packet, not
that the recipient has it, so it is not a terminal state and there is no reason to stop watching.

The state still always resolves, which is what makes this safe rather than a hang. Either the
destination ACKs, which the isFromDest branch takes and which clears the flag, or our own router
runs out of retries and NAKs MAX_RETRANSMIT, which lands in the explicit-failure branch and clears
it there.

Nothing can be downgraded. A duplicate relay ACK can only rewrite RELAYED with RELAYED, and once the
destination ACK has set ACKED the flag is clear and the guard rejects everything after it.

waitingForAck has no other consumers: it is set when sending, and read by wantPacket() to decide
whether ROUTING_APP packets are interesting. Holding it longer only means we keep listening.

Overlap with #10921

That PR restructures this area and partially addresses the symptom: its RoutingModule hook is not
gated on waitingForAck and matches by packetId, so the stored row does upgrade RELAYED to ACKED.
This patch is written against current develop, where that has not landed.

If #10921 goes in first, this needs rewriting against its ackStatusForRoutingResult /
updateAckStatusFromRouting mechanism rather than the getMessages().back() block. The banner state
(this->ack, this->incoming) would still need the change here, since that hook does not touch it.

Worth checking there too: updateAckStatusFromRouting has no monotonicity guard, so a late or
duplicate relay ACK could downgrade an ACKED row. The approach in this PR avoids that by keeping the
guard as the thing that decides when the exchange is over.

Testing: builds clean on heltec-v4 and seeed-xiao-s3. Full native suite matches a same-commit
baseline at 887 cases, 3 failed, 875 succeeded. The three are test_fscommon_getfiles,
test_lsm_standalone and test_gps_update_scheduling, all of which fail on clean develop too. Note
those three are unstable run to run: the same commit produced 888/6 and then 887/3 on consecutive
runs here, with test_fscommon_getfiles failing on leftover filesystem state, so compare against a
same-commit run rather than a remembered number.

Not validated on hardware. Reproducing it needs a third node to relay, and my bench is two co-located
nodes that hear each other directly, so no relay ACK is ever generated.

🤖 Generated with Claude Code

🤝 Attestations

  • I have tested that my proposed changes behave as described.
  • I have tested that my proposed changes do not cause any obvious regressions on the following devices:
    • Heltec (Lora32) V3
    • LilyGo T-Deck
    • LilyGo T-Beam
    • RAK WisBlock 4631
    • Seeed Studio T-1000E tracker card
    • Other: compile-only on Heltec V4 and Seeed XIAO ESP32-S3; needs a three-node bench to exercise

The routing handler is guarded by waitingForAck, and the relay-ACK branch clears
it. So the first node that forwards our DM ends our interest in the exchange:
the destination's own ACK arrives later, fails the guard at the top of the
function, and is dropped. The message shows RELAYED permanently even though it
was delivered, and this->ack, this->incoming and the stored AckStatus all stay
at the relay result.

Leave waitingForAck set on a relay ACK. A relay ACK says someone forwarded the
packet, not that the recipient has it, so it is not a terminal state and there is
no reason to stop watching.

The state still always resolves. Either the destination ACKs, which the
isFromDest branch takes and which clears the flag, or our own router runs out of
retries and NAKs MAX_RETRANSMIT, which lands in the explicit-failure branch and
clears it there. A later duplicate relay ACK can only rewrite RELAYED with
RELAYED, and nothing can downgrade an ACKED row, because the destination ACK
clears the flag and the guard then rejects everything that follows.

waitingForAck has no other consumers: it is set when sending and read by
wantPacket() to decide whether ROUTING_APP packets are interesting. Holding it
longer only means we keep listening.
@github-actions

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Note

Building this pull request… the flash button, badges and supported-board
list will appear here automatically once CI finishes.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4617d7c6-be91-4c2f-aaee-b65dd3a3fd07

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@h3lix1
h3lix1 force-pushed the fix/cannedmessage-relay-ack-latch branch from b52e4c4 to 81c8fe6 Compare August 13, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant