Skip to content

fix: send on-demand history sync request to own primary device - #218

Open
jaquisonsferreira wants to merge 2 commits into
evolution-foundation:mainfrom
jaquisonsferreira:fix/history-sync-peer-message
Open

jaquisonsferreira wants to merge 2 commits into
evolution-foundation:mainfrom
jaquisonsferreira:fix/history-sync-peer-message

Conversation

@jaquisonsferreira

@jaquisonsferreira jaquisonsferreira commented Oct 11, 2026 •

Copy link
Copy Markdown

Problem

POST /chat/history-sync returns 200, but no HistorySync event (type ON_DEMAND) ever arrives, so on-demand history never works.

HistorySyncRequest sends the peer data operation built by BuildHistorySyncRequest to messageInfo.Chat with SendRequestExtra{Peer: true}. A peer message is meant for the account's own primary device, so the request ends up addressed to the contact and the phone never answers.

whatsmeow documents that the built message must be sent with Client.SendPeerMessage, which targets the account's own JID:

func (cli *Client) SendPeerMessage(ctx context.Context, message *waE2E.Message) (SendResponse, error) {
	ownID := cli.getOwnID().ToNonAD()
	...
	return cli.SendMessage(ctx, ownID, message, SendRequestExtra{Peer: true})
}

Fix

  • Send the request with SendPeerMessage through a small historySyncSender interface (satisfied by *whatsmeow.Client).
  • The chat JID, oldest message id, fromMe, timestamp and count are still taken from messageInfo, so the request payload is unchanged.

Tests

pkg/chat/service/history_sync_test.go checks that the request goes through the peer sender with the expected chat, anchor message, timestamp and count, and that send errors are propagated.

Verified in production

Running this patch on a real instance, calling /chat/history-sync with the oldest known message of a chat (LID addressed) now produces the HistorySync ON_DEMAND webhook with the previous messages of that chat. Before the patch the same call returned 200 and nothing else happened.

Summary by Sourcery

Send on-demand history sync requests through the client’s peer messaging path to enable reliable synchronization from the primary device.

Bug Fixes:

  • Route on-demand history sync requests to the account’s own primary device so the corresponding history sync events are delivered.
  • Propagate errors returned while sending peer history sync requests.

Enhancements:

  • Preserve the existing history sync request payload while introducing a peer-sender abstraction.

Tests:

  • Add coverage for peer delivery, request parameters, response handling, and send-error propagation.

Jaquison Ferreira added 2 commits October 10, 2026 22:20
HistorySyncRequest sent the peer data operation to the chat JID, so the
request reached the contact instead of the account's primary phone and
no HistorySync ON_DEMAND event was ever emitted. Use SendPeerMessage,
which targets the account's own JID as whatsmeow requires.
@sourcery-ai

sourcery-ai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Fix on-demand history sync by building the same request payload but sending it through whatsmeow’s SendPeerMessage API, which targets the account’s own primary device rather than the chat contact. The change is isolated behind a testable sender interface and includes payload and error-propagation tests.

Sequence diagram for the on-demand history sync request

sequenceDiagram
    participant API as Chat API
    participant Service as chatService
    participant Sender as historySyncSender
    participant Primary as Own primary device
    participant Contact as Chat contact

    API->>Service: HistorySyncRequest(data, instance)
    Service->>Sender: BuildHistorySyncRequest(messageInfo, count)
    Sender-->>Service: waE2E.Message
    Service->>Sender: SendPeerMessage(ctx, message)
    Sender->>Primary: Send peer request
    Primary-->>Sender: SendResponse
    Sender-->>Service: SendResponse or error
    Service-->>API: response or propagated error

    Note over Contact: Request is no longer sent to the chat contact
Loading

File-Level Changes

Change Details Files
Route on-demand history-sync peer requests to the account’s own primary device.
  • Replace direct peer-flagged delivery to the chat JID with SendPeerMessage.
  • Preserve the existing request payload derived from message metadata and count.
  • Introduce a small sender interface and helper to isolate request construction and delivery.
pkg/chat/service/chat_service.go
Add focused coverage for peer routing, payload preservation, and error handling.
  • Record the peer message and verify chat JID, oldest message ID, count, and timestamp.
  • Verify the send error is returned to the caller.
pkg/chat/service/history_sync_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai 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.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@vicente-r-junior

Copy link
Copy Markdown

We hit this bug in production on v0.7.2 and can confirm the fix here is the right one.

Context: our bot's number was logged out for ~45h ("Logged out for reason 401: logged out from another device", primary phone was off). After re-pairing, we tried to recover the gap with POST /chat/history-sync, anchored on a real group message (ID and timestamp checked against our DB).

What happened:

  • The API returned 200, but the timings showed GetParticipants + GroupEncrypt, so the request was sent to the group participants, not to our own primary device.
  • The phone never answered with an ON_DEMAND HistorySync. We waited several minutes with HISTORY_SYNC subscribed.

Root cause is exactly what this PR addresses: chat_service.go calls SendMessage(ctx, messageInfo.Chat, ..., SendRequestExtra{Peer: true}) instead of sending the peer message to our own JID (SendPeerMessage).

Without this fix there is no way to recover messages from a disconnection window. A merge and a release would be much appreciated. Thanks for the work! 🙏

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.

2 participants