Skip to content

fix(blocklist): include LID + pn_jid in block IQ (modern WA) - #600

Merged
jlucaso1 merged 3 commits into
oxidezap:mainfrom
Salientekill:fix/blocklist-lid-pn-jid
Apr 27, 2026
Merged

fix(blocklist): include LID + pn_jid in block IQ (modern WA)#600
jlucaso1 merged 3 commits into
oxidezap:mainfrom
Salientekill:fix/blocklist-lid-pn-jid

Conversation

@Salientekill

Copy link
Copy Markdown
Contributor

Summary

Modern WhatsApp servers (2026+) reject the legacy block stanza with code=400 bad-request:

<item action=\"block\" jid=\"...@s.whatsapp.net\"/>

After the LID rollout the blocklist IQ now requires both halves of the LID↔PN pair on the wire:

<item action=\"block\" jid=\"<lid>@lid\" pn_jid=\"<phone>@s.whatsapp.net\"/>

Reproduced empirically — every call to Blocking::block against a real account currently logs:

WARN  received a server error response: code=400, text='bad-request'

Cross-referenced against Baileys' updateBlockStatus (Socket/chats.ts), which:

  1. resolves LID↔PN through its mapping store before sending,
  2. always puts the LID in jid,
  3. for block adds pn_jid with the PN,
  4. for unblock keeps only the LID jid.

This PR mirrors that behavior.

Changes

  • BlocklistItemRequest gains an optional pn_jid attribute and a new block_with_pn(lid, pn) constructor. The legacy block(jid) / unblock(jid) constructors keep their signatures for callers that already pass a pre-resolved JID.
  • Blocking::block and Blocking::unblock now use client.get_lid_pn_entry to resolve the missing side regardless of whether the caller passed LID or PN, then send the modern stanza:
    • blockjid=LID, pn_jid=PN
    • unblockjid=LID only
  • If no LID↔PN mapping is available, both methods now return a structured IqError::ServerError instead of silently sending a malformed stanza — the bug was previously invisible to callers because the server-side 400 isn't surfaced as an error in some flows.

Test plan

  • cargo test -p wacore --lib blocklist — 9 passed (added test_block_with_pn_emits_pn_jid_attr and test_unblock_omits_pn_jid_attr)
  • cargo clippy --all --tests — clean
  • cargo fmt --all — applied
  • Manual: previously-failing Blocking::block against a live account now succeeds and shows up in WA Web's blocklist

Notes for reviewers

  • BlocklistItemRequest::block(&jid) (no pn_jid) is kept so existing callers compile, but in practice the modern server will reject any IQ produced this way. The high-level Blocking::block always routes through block_with_pn. We could deprecate the no-pn_jid form in a follow-up.
  • Blocking::unblock still only ships LID — Baileys does the same and the server accepts it.

Modern WhatsApp servers (2026+) reject `<item action="block" jid="...@s.whatsapp.net"/>`
with `code=400 bad-request`. After the LID rollout the blocklist IQ requires
both halves of the LID↔PN pair:

  <item action="block" jid="<lid>@lid" pn_jid="<phone>@s.whatsapp.net"/>

Verified against Baileys' `updateBlockStatus` (`Socket/chats.ts`), which now
resolves both sides via its LID mapping store before sending.

Changes:

* `BlocklistItemRequest` gains an optional `pn_jid` attribute and a new
  `block_with_pn(lid, pn)` constructor. Old `block(jid)` / `unblock(jid)`
  keep working for callers that already have the right shape.
* `Blocking::block` and `Blocking::unblock` now resolve LID↔PN through
  `client.get_lid_pn_entry` regardless of which side the caller passes,
  then send the modern stanza:
  - block:   `jid=LID, pn_jid=PN`
  - unblock: `jid=LID` (PN not required by the server for removals)
* Returns a structured `IqError::ServerError` when no LID↔PN mapping is
  available — block silently no-op'd before, masking the bug.

Tests:
* `test_block_with_pn_emits_pn_jid_attr` — asserts both attrs on the wire
* `test_unblock_omits_pn_jid_attr`       — asserts unblock keeps the old shape
@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a7666eb5-072d-40ec-8045-89111ec2545a

📥 Commits

Reviewing files that changed from the base of the PR and between abc2e4d and c24d94e.

📒 Files selected for processing (2)
  • src/features/blocking.rs
  • wacore/src/iq/blocklist.rs

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Block/unblock now normalize and resolve different contact identifier formats (PN or LID) before sending, reducing mapping errors and improving failure handling.
  • New Features

    • Block requests now include an additional server-side identifier when available to meet updated stanza requirements.
  • Tests

    • Added unit tests verifying block requests include the extra identifier and unblock requests omit it.

Walkthrough

Block/unblock now normalize input JIDs to non-AD form and resolve LID↔PN mappings via a new resolver that accepts either PN or LID. Block sends an IQ using block_with_pn(lid, pn) (including pn_jid); unblock resolves to the LID and sends unblock with the LID only. Logging no longer prints the raw input JID.

Changes

Cohort / File(s) Summary
Block/Unblock Control Logic
src/features/blocking.rs
Add internal resolver that accepts PN or LID and returns (lid_jid, pn_jid); normalize input with to_non_ad(); call UpdateBlocklistSpec::block_with_pn(&lid_jid, &pn_jid) for block and UpdateBlocklistSpec::unblock(&lid_jid) for unblock; adjust error handling and remove logging of provided jid.
Blocklist Wire Format Support
wacore/src/iq/blocklist.rs
BlocklistItemRequest gains pub pn_jid: Option<Jid>; add block_with_pn(lid, pn_jid) constructor and UpdateBlocklistSpec::block_with_pn; unit tests added to assert pn_jid serializes for block and is omitted for unblock.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Feature as features/blocking.rs
    participant Store as client.get_lid_pn_entry
    participant BlockSpec as wacore::UpdateBlocklistSpec
    participant IQ as IQ Handler

    rect rgba(33, 150, 243, 0.5)
    Client->>Feature: block(jid)
    Feature->>Feature: jid.to_non_ad()
    Feature->>Store: get_lid_pn_entry(normalized_jid)
    Store-->>Feature: (lid_jid, pn_jid) or error
    alt mapping found
        Feature->>BlockSpec: block_with_pn(&lid_jid, &pn_jid)
        BlockSpec->>IQ: send IQ with pn_jid attribute
        IQ-->>Feature: success
    else mapping missing / invalid
        Feature-->>Client: return IqError::ServerError
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding LID and pn_jid to block IQ for modern WhatsApp servers, which is the core objective of the PR.
Description check ✅ Passed The description is directly related to the changeset, providing context about modern server requirements, changes made, test plan, and reviewer notes.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai
coderabbitai Bot requested a review from jlucaso1 April 27, 2026 19:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/blocking.rs`:
- Around line 31-64: Extract the duplicated LID/PN mapping logic into a single
helper method (e.g. impl method resolve_lid_pn(&self, bare: &Jid) -> Result<(Jid
/*lid*/, Jid /*pn*/), IqError>) that encapsulates the two branches: call
self.client.get_lid_pn_entry(bare).await, map client errors into
IqError::ServerError with the existing messages, return (bare,
Jid::pn(entry.phone_number)) when bare.is_lid(), return (Jid::lid(entry.lid),
bare) when bare.is_pn(), and return the "neither PN nor LID" IqError for other
cases; then replace the duplicate blocks in block and unblock (and the similar
code at the 79-100 region) to call resolve_lid_pn and use its returned (lid_jid,
pn_jid).
- Around line 42-43: The code currently includes raw PN/LID identifiers (e.g.,
using the variable bare in text: format!("no LID↔PN mapping for {bare}")) which
leaks user IDs; replace those raw identifiers with a redacted representation
everywhere referenced (lines around the format call and the other occurrences at
the listed locations). Implement or call a small helper (e.g., redact_id or
redact) to convert bare into a non-identifying token (like "<redacted>", a fixed
prefix+hash, or masked string) and use that in all user-facing logs/errors such
as the message in the error return and the other similar format! calls so the
message becomes generic (e.g., "no LID↔PN mapping for <redacted>") while keeping
code paths and error types unchanged (update references to bare inside format!
calls and any log invocations).

In `@wacore/src/iq/blocklist.rs`:
- Around line 28-33: The blocklist comment includes time-bound operational
details (e.g., "2026-04") and verbose protocol explanation; replace it with a
single concise "why" line such as "Wire requires jid in LID and pn_jid for
blocking" immediately above the ProtocolNode derive, and move the empirical
verification and dated specifics into docs or integration tests (e.g.,
compatibility notes or a test file). Apply the same trim-and-move change to the
other similar comment blocks referenced (the comments around the other
ProtocolNode or struct docs) so comments remain brief and non-time-bound while
detailed evidence lives in docs/tests.
🪄 Autofix (Beta)

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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8fd8c629-a7c7-43fb-adae-8386efe3306e

📥 Commits

Reviewing files that changed from the base of the PR and between b79a9c5 and 6ed1c3c.

📒 Files selected for processing (2)
  • src/features/blocking.rs
  • wacore/src/iq/blocklist.rs

Comment thread src/features/blocking.rs Outdated
Comment thread src/features/blocking.rs Outdated
Comment thread wacore/src/iq/blocklist.rs Outdated
* Extract `Blocking::resolve_lid_pn` to share the LID↔PN resolution
  between `block` and `unblock` (was duplicated, would drift).
* Stop interpolating raw PN/LID identifiers into error messages and
  debug logs — the `Blocking` API now returns generic "blocklist:
  no LID↔PN mapping for provided jid" / "lookup failed" / "neither
  PN nor LID" so user IDs don't end up in surfaces that get logged
  or surfaced upstream.
* Trim the protocol comment on `BlocklistItemRequest` and the
  `block_with_pn` constructors down to a single "why" line; drop
  dated empirical notes (those belong in tests, which already cover
  the wire shape).
…k_with_pn

Preflight failures (no LID↔PN mapping, jid neither PN nor LID, backend
lookup error) were emitted as `IqError::ServerError { code: 0 }`, which
collides with real server codes and discards the backend error. Switch
to `IqError::EncodeError(anyhow::Error)` — the existing variant for
"couldn't build the IQ" — and preserve the backend source.

Also collapse the two LID/PN branches into a single `get_lid_pn_entry`
call and have `BlocklistItemRequest::block_with_pn` delegate to
`Self::new` instead of repeating the field list.
@jlucaso1
jlucaso1 merged commit d1d5713 into oxidezap:main Apr 27, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants