> ## Documentation Index
> Fetch the complete documentation index at: https://whatsapp-rust.jlucaso.com/llms.txt
> Use this file to discover all available pages before exploring further.

# June 11, 2026 — PDO placeholder-resend: at-most-once per message

> Adds a pdo_requested memo cache that caps PDO placeholder-resend requests to one per message, skips a redundant migration retry decrypt when nothing moved, and adjusts noisy log levels.

## Bug Fix

**PDO placeholder-resend: at-most-once per message ([#841](https://github.com/oxidezap/whatsapp-rust/pull/841))**

A peer device with cloned Signal state could redeliver the same undecryptable message every \~11 seconds. Each copy triggered a PDO placeholder-resend request to our own phone — the `pdo_pending_requests` dedup only covered in-flight requests, so it emptied the moment the phone answered (\~800 ms), and the next copy immediately opened a new one. In a three-hour storm this produced \~700 redundant requests with no benefit: the phone had already answered without content, so re-asking could not produce anything new.

**One request per message.** A new `pdo_requested` memo cache (24h TTL, 512 entries) gates `send_pdo_placeholder_resend_request` — mirroring `WAWebNonMessageDataRequestPlaceholderMessageResendUtils`, which uses a session-lifetime set for the same purpose. The memo slot is released if the send itself fails, so a transient error does not permanently block recovery. A content-less phone response leaves the memo in place (the phone has nothing to share for this message; re-asking on the next redelivery cannot help).

**Skip migration retry decrypt when nothing moved.** `migrate_signal_sessions_on_lid_discovery` now returns `bool` indicating whether any sessions moved into a LID slot. When it returns `false`, the subsequent retry decrypt in `try_pn_to_lid_migration_decrypt` is skipped: the Signal state is unchanged, so the retry would fail identically and only add a second decrypt error to the log for every redelivered copy.

**Log-level adjustments.** Three lines that fired once per redelivered copy are brought in line with WA Web's telemetry handling:

* "Skipping skmsg decryption" → `debug` (WA Web's `canDecryptNext` skips silently after a retryable pkmsg failure)
* "missing message content" on a PDO response → `info` (WA Web counts this outcome in telemetry only, no warning)
* "Max retries reached" at the PDO fallback → `debug` (the high-retry `warn!` already fired on the way to the cap)

With these changes, the same storm would produce 1 PDO request, 1 decrypt error per copy, and the existing retry receipt cap — instead of thousands of WARN/ERROR lines and hundreds of peer messages.

## Breaking changes

**`CacheConfig` gains a new field (`whatsapp-rust`)**

`CacheConfig` now has a `pdo_requested` field (default: 24h TTL, 512 entries). Struct literals that spell out every field rather than using `..Default::default()` will fail to compile.

```rust theme={null}
// Update struct literals that do not use ..Default::default():
let config = CacheConfig {
    // your overrides ...
    ..Default::default() // pdo_requested gets its default; no action required
};
```

To tune the memo TTL or capacity:

```rust theme={null}
use std::time::Duration;
use whatsapp_rust::{CacheConfig, CacheEntryConfig};

let config = CacheConfig {
    // Extend the TTL beyond the 24h default if you want the memo to survive
    // across longer offline gaps without re-asking the phone.
    pdo_requested: CacheEntryConfig::new(Some(Duration::from_secs(48 * 3600)), 512),
    ..Default::default()
};
```

**`MemoryDiagnostics` gains a new field (`whatsapp-rust`)**

`MemoryDiagnostics` now has a `pdo_requested: u64` field. Code that exhaustively pattern-matches or constructs `MemoryDiagnostics` directly will need updating.
