docs: document Event::EncDecryptFailed (whatsapp-rust#1261) - #504
Conversation
- Add Event::EncDecryptFailed / EncDecryptFailedLease to concepts/events.mdx and api/client.mdx, mirroring the existing DecryptedPayload documentation - Document EncDecryptFailureReason's 14 variants and the event's caveats (not a display signal, not a loss report, repeats on redelivery, duplicates emit neither event) - Document the breaking wacore::bot_message::decrypt_bot_message signature change (anyhow::Result -> Result<T, BotMessageError>) in api/errors.mdx Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
📝 WalkthroughWalkthroughThe documentation adds lease-controlled ChangesEncryption failure events
Typed bot-message errors
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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. Comment |
|
| Filename | Overview |
|---|---|
| api/client.mdx | Documents the new forwarding lease and correctly registers event handlers before lease acquisition in both affected examples. |
| api/errors.mdx | Documents the typed bot-message error hierarchy, stage classification, and migration from anyhow::Result. |
| concepts/events.mdx | Adds the new event payload, all failure reasons, emission semantics, caveats, and API cross-links without an eligible follow-up defect. |
Sequence Diagram
sequenceDiagram
participant App
participant Client
participant Bus as Event bus
participant Handler
App->>Client: register_handler(handler)
App->>Client: acquire_enc_decrypt_failed_forwarding()
Client-->>App: EncDecryptFailedLease
Client->>Client: "Attempt each <enc> decryption"
alt Decryption fails while lease is held
Client->>Bus: Event::EncDecryptFailed
Bus->>Handler: Dispatch event
else No lease is held
Client->>Client: Skip event construction
end
Reviews (4): Last reviewed commit: "docs(events): fix at-most-once correlati..." | Re-trigger Greptile
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@api/client.mdx`:
- Around line 2074-2081: Rewrite the EncDecryptFailedLease documentation using
direct second-person guidance and active voice. Split the activation
requirement, per-enc scope and enc_index numbering, separate-counter behavior,
RAII lifetime, weak-reference behavior, and no-lease cost guarantee into short
sentences with one contract detail per sentence; preserve all existing
semantics.
In `@api/errors.mdx`:
- Around line 902-920: Update both decrypt_bot_message calls in the before and
after examples to pass the documented four arguments in order: message_secret,
enc_iv, enc_payload, and ctx. Keep the existing match handling unchanged.
- Line 582: Format the BotMessageError type name as inline code in the heading
by wrapping it with backticks, while preserving the existing heading level and
text.
In `@concepts/events.mdx`:
- Line 2905: Update the emitted-event description for
PlaintextUnusable/EncDecryptFailed to state that the <enc> did not produce a
usable message, rather than claiming it produced no plaintext; preserve the
existing per-<enc> granularity and DecryptedPayload indexing details.
- Line 2980: Update the “Repeats” documentation to state that each event is per
encryption node and at-most-once deduplication must correlate on both info.id
and enc_index. Preserve the existing redelivery behavior while clarifying that
multiple failing <enc> nodes in one stanza produce distinct events.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f448cc25-5915-4007-b183-cbfa1db18810
📒 Files selected for processing (3)
api/client.mdxapi/errors.mdxconcepts/events.mdx
| Acquire a lease that keeps [`Event::EncDecryptFailed`](/concepts/events#encdecryptfailed) enabled for one consumer. The lease is necessary but not sufficient: a handler that has narrowed its `interest()` away from the default `EventInterest::ALL` also needs `EventKind::EncDecryptFailed` added back in, or it won't see the event even while a lease is held. This is the failing counterpart of [`acquire_decrypted_payload_forwarding`](#acquire_decrypted_payload_forwarding) — same per-`<enc>` granularity, same `enc_index` numbering — but tracked by a separate counter on purpose: a consumer that wants both halves of a stanza's decryption holds both leases, one that wants only failures does not make the success path clone plaintext, and one that wants only successes pays nothing extra on the failure paths. | ||
|
|
||
| <ResponseField name="EncDecryptFailedLease" type="EncDecryptFailedLease"> | ||
| RAII lease. `Event::EncDecryptFailed` stays enabled until every acquired lease is dropped — hold it for as long as you want the event forwarded. The lease holds only a weak client reference, so it cannot keep the client alive. | ||
| </ResponseField> | ||
|
|
||
| <Note> | ||
| While no lease is held, nothing is emitted and nothing is built — each failure branch costs one relaxed atomic load. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Use direct reader guidance and split the contract details.
Lines 2074-2081 use third-person consumers. They also combine activation, event scope, numbering, and cost behavior in long sentences. Use “you” and separate each guarantee into its own sentence.
Proposed wording
- Acquire a lease that keeps [`Event::EncDecryptFailed`](/concepts/events#encdecryptfailed) enabled for one consumer. The lease is necessary but not sufficient: a handler that has narrowed its `interest()` away from the default `EventInterest::ALL` also needs `EventKind::EncDecryptFailed` added back in, or it won't see the event even while a lease is held.
+ Acquire a lease to keep [`Event::EncDecryptFailed`](/concepts/events#encdecryptfailed) enabled for your consumer. The lease does not enable delivery by itself. If you narrow your handler's `interest()` away from `EventInterest::ALL`, add `EventKind::EncDecryptFailed` back in.As per coding guidelines, use active voice and second-person wording, and keep one idea per sentence.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@api/client.mdx` around lines 2074 - 2081, Rewrite the EncDecryptFailedLease
documentation using direct second-person guidance and active voice. Split the
activation requirement, per-enc scope and enc_index numbering, separate-counter
behavior, RAII lifetime, weak-reference behavior, and no-lease cost guarantee
into short sentences with one contract detail per sentence; preserve all
existing semantics.
Source: Coding guidelines
|
|
||
| Both variants mean the peer sent a target message key that cannot be attributed — retrying the same envelope yields the same result. `MessageEditError` is re-exported from the crate root as `whatsapp_rust::MessageEditError`. | ||
|
|
||
| ### BotMessageError |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Format the type name as code.
Use ### \BotMessageError`` for this code reference in the heading. As per coding guidelines, use code formatting for code references.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@api/errors.mdx` at line 582, Format the BotMessageError type name as inline
code in the heading by wrapping it with backticks, while preserving the existing
heading level and text.
Source: Coding guidelines
- Register the event handler before acquiring the lease in the EncDecryptFailed example, closing the window where an early failure could be dispatched to no consumer (Greptile). - Fix the decrypt_bot_message migration example: it only passed 2 of the function's 4 arguments and wouldn't compile (CodeRabbit). - Correct "produced no plaintext" to "did not produce a usable message" — PlaintextUnusable fires after decryption did produce plaintext (CodeRabbit). - Note that at-most-once correlation needs enc_index alongside info.id, since a stanza can have more than one failing <enc> (CodeRabbit). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GzZt1n6RgVCS4LGVywtS7p
|
Pushed 186d042 addressing review feedback: Fixed:
Skipped:
Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b234bde69
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| ### `EncDecryptFailed` | ||
|
|
||
| **Emitted:** One `<enc>` that produced no plaintext, and why — the failing half of what [`DecryptedPayload`](#decryptedpayload) reports for the succeeding half, at the same granularity (per `<enc>`, not per message) and under the same numbering (`enc_index` comes from the same enumeration as `DecryptedPayload::enc_index`, so the two events index one stanza and not two). To receive this event, hold a lease from `client.acquire_enc_decrypt_failed_forwarding()` and include `EventKind::EncDecryptFailed` in your handler's `interest()`. While no lease is held, nothing is emitted and nothing is built. |
There was a problem hiding this comment.
Account for failures after plaintext is produced
When decoding fails after successful decryption, PlaintextUnusable explicitly emits both EncDecryptFailed and DecryptedPayload for the same <enc> (as documented at line 2970). Describing these as mutually exclusive failing and succeeding halves where the failure produced no plaintext can therefore mislead consumers; define this event as failure to produce a usable message instead.
Useful? React with 👍 / 👎.
| // After — match specific variants, or classify by stage | ||
| use wacore::bot_message::{BotMessageError, BotMessageFailure}; | ||
|
|
||
| match decrypt_bot_message(&payload, &secret) { |
There was a problem hiding this comment.
Pass all four arguments in the migration example
The documented low-level signature is decrypt_bot_message(message_secret, enc_iv, enc_payload, ctx) in api/signal.mdx:554, but both calls in this migration example pass only payload and secret. Anyone adapting this example gets an arity error unrelated to the return-type migration; retain all four arguments and change only the error handling.
Useful? React with 👍 / 👎.
| let _lease = client.acquire_enc_decrypt_failed_forwarding(); | ||
|
|
||
| let (handler, event_rx) = ChannelEventHandler::new(); | ||
| client.register_handler(handler); |
There was a problem hiding this comment.
Register the handler before enabling forwarding
If an inbound <enc> fails between acquiring this lease and registering the channel handler, forwarding is already active and that event is dispatched without reaching the new receiver. The adjacent SentFrame example explicitly avoids the same race by registering first; reorder these calls so consumers following this example do not miss initial failures.
Useful? React with 👍 / 👎.
| <Note> | ||
| - **Not a display signal.** Whether to show the user a placeholder is [`UndecryptableMessage`](#undecryptablemessage), which is per *message*, deduplicated by `(chat, id)`, and carries the server's `decrypt-fail` hint. `EncDecryptFailed` is per `<enc>`, is not deduplicated, and answers a different question. | ||
| - **Not a loss report.** Most reasons are recoverable — the client may already have asked the sender to resend — and this event says nothing about whether a retry went out or succeeded later. | ||
| - **Repeats.** A redelivered stanza that fails again emits it again, once per `<enc>` per delivery. Correlate on `info.id` if you want at-most-once. |
There was a problem hiding this comment.
Include
enc_index in the deduplication key
For a stanza containing multiple failing <enc> nodes, every event shares info.id but has a distinct enc_index. Deduplicating only on the recommended message ID therefore drops legitimate failures from the first delivery, not just repeats; the key needs at least the message identity and enc_index.
Useful? React with 👍 / 👎.
|
@chatgpt-codex-connector all four of these were already caught by Greptile/CodeRabbit on the same commit and fixed in 186d042 (handler-before-lease ordering, the 4-arg Generated by Claude Code |
|
To use Codex here, create an environment for this repo. |
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…ayload_forwarding cubic flagged that the acquire_decrypted_payload_forwarding example still acquired its lease before registering the handler -- the identical race the EncDecryptFailed example was just fixed for. Reorders it to match, and adds the explanatory comment already used in acquire_sent_frame_forwarding to both examples for consistency. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GzZt1n6RgVCS4LGVywtS7p
|
Good catch — pushed f9a782c. Fixed the identical lease-before-handler ordering in the Generated by Claude Code |
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Auto-approved: Documentation-only PR: adds Event::EncDecryptFailed and BotMessageError docs to .mdx files and fixes ordering in API examples. No code, config, schema, or rollout changes, so no human-owned product or operational tradeoff remains.
Re-trigger cubic
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f9a782c83f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <Note> | ||
| - **Not a display signal.** Whether to show the user a placeholder is [`UndecryptableMessage`](#undecryptablemessage), which is per *message*, deduplicated by `(chat, id)`, and carries the server's `decrypt-fail` hint. `EncDecryptFailed` is per `<enc>`, is not deduplicated, and answers a different question. | ||
| - **Not a loss report.** Most reasons are recoverable — the client may already have asked the sender to resend — and this event says nothing about whether a retry went out or succeeded later. | ||
| - **Repeats.** A redelivered stanza that fails again emits it again, once per `<enc>` per delivery. Correlate on `info.id` and `enc_index` if you want at-most-once per `<enc>`. |
There was a problem hiding this comment.
Include chat and sender in the deduplication key
When different chats or senders reuse the same stanza ID, this updated recommendation still collapses unrelated failure events even after adding enc_index. Fresh repository evidence in advanced/inbound-durability.mdx:125 explicitly states that stanza IDs are unique only within a (chat, sender) pair, so the at-most-once key should include info.source.chat, info.source.sender, info.id, and enc_index.
Useful? React with 👍 / 👎.
Codex flagged that info.id alone isn't globally unique -- only within a (chat, sender) pair, per the idempotency key already established in advanced/inbound-durability.mdx. My earlier fix (info.id + enc_index) was still incomplete. Match the established (chat, sender, id) triplet, plus enc_index for this per-enc event. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GzZt1n6RgVCS4LGVywtS7p
|
Good catch, verified against Generated by Claude Code |
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Auto-approved: Documentation-only: adds docs for the existing Event::EncDecryptFailed and the already-merged wacore BotMessageError change, plus example ordering fixes. No behavior or tradeoffs here; the API decisions belonged to code PR #1261.
Re-trigger cubic
Documents the new library-only event added in
oxidezap/whatsapp-rust#1261("feat(client): report which<enc>failed to decrypt, and why"), plus the two smaller API changes that PR made towacore.Summary
Event::EncDecryptFailed/EncDecryptFailedLeasedocumentation toconcepts/events.mdxandapi/client.mdx, mirroring the existingEvent::DecryptedPayload/DecryptedPayloadLeasewrite-up (same lease-gating pattern, same "nothing is emitted/built while unheld" cost model).EncDecryptFailureReasonvariants (condensed from the source doc comments) anddecryption_was_attempted().Event::UndecryptableMessage), not a loss report, repeats on redelivery, and a genuine protocol duplicate emits neitherEncDecryptFailednorDecryptedPayload.wacore's public API inapi/errors.mdx:wacore::bot_message::decrypt_bot_messagenow returnsResult<T, BotMessageError>instead ofanyhow::Result<T>. AddedBotMessageError/BotMessageFailuretype definitions, an error-hierarchy entry, a domain-error-table row (flagged as living in thewacorecrate, notwhatsapp_rust), and a "From PR #1261" migration-guide subsection matching the doc's existing pattern for breaking changes.api/wacore.mdxuntouched: it doesn't document thebot_messagemodule orwacore-libsignal's error surface at all today, so adding a new section there would be a bigger structural change than a documentation follow-up warrants —api/errors.mdxwas the better fit for the one breaking change that needed calling out.api/events.mdxuntouched — that file documents the unrelated "Events" feature (calendar-style event messages/RSVPs), not theEventenum.SignalProtocolError::is_stored_session_corruption()(also added in #1261) undocumented:wacore-libsignal's public error surface isn't documented at this granularity anywhere in this site today, so adding a section for one new method would be introducing a new kind of documentation rather than extending an existing one.Files touched
concepts/events.mdx— newEvent::EncDecryptFailedentry (enum listing + full## Enc decrypt failure eventssection)api/client.mdx— new### acquire_enc_decrypt_failed_forwardingsectionapi/errors.mdx— newBotMessageError/BotMessageFailuredocumentation, breaking-change callout, and migration guide entryNot touched:
changelog/(maintained by hand) andpt/(translation sync is handled separately).Ref: oxidezap/whatsapp-rust#1261
Generated by Claude Code
Summary by cubic
Documents the new
Event::EncDecryptFailedand its lease, with clear failure reasons, usage, and caveats. Also calls out the breaking change inwacore::bot_message::decrypt_bot_message, fixes lease-ordering in examples, and corrects the at-most-once correlation key.New Features
Event::EncDecryptFailed(per-<enc>granularity, sameenc_indexasDecryptedPayload, “did not produce a usable message” phrasing).client.acquire_enc_decrypt_failed_forwarding(), and includeEventKind::EncDecryptFailedininterest(). No lease = no event, no work.EncDecryptFailureReasonvariants anddecryption_was_attempted(), plus caveats: not a display signal; not a loss report; repeats on redelivery; true duplicates emit neither event; order followsenc_index; at-most-once correlation needs(chat, sender, id, enc_index).acquire_decrypted_payload_forwardingandacquire_enc_decrypt_failed_forwardingnow register the handler before acquiring the lease, with a note explaining why.Migration
wacore::bot_message::decrypt_bot_message: return type changed fromanyhow::Result<T>toResult<T, BotMessageError>.BotMessageErrorvariants or usestage()to classify viaBotMessageFailure(Envelope|Secret|Authentication).Written for commit a1ea9c9. Summary will update on new commits.
Summary by CodeRabbit