Skip to content

WhatsApp: multiple channel_instances share one whatsmeow_device row #1064

@kamushadenes

Description

@kamushadenes

Bug

Each WhatsApp channel_instance calls container.GetFirstDevice(ctx) on a shared sqlstore.Container (one Postgres DB). When two or more WhatsApp instances exist, every channel binds to the same first device row in whatsmeow_device and connects as the same WhatsApp account, regardless of the instance name or agent it is wired to.

Repro

  1. Configure one WhatsApp channel_instance (e.g. whatsapp → agent A). Pair via the QR wizard. Works fine.
  2. Add a second WhatsApp channel_instance (e.g. whatsapp-B → agent B) intending to bind a different phone number.
  3. Restart the gateway (or hit "Start QR" on the second instance).

Observed

time=... level=INFO msg="channel instance loaded" name=whatsapp        type=whatsapp ...
time=... level=INFO msg="channel instance loaded" name=whatsapp-B      type=whatsapp ...
time=... level=WARN msg="whatsapp QR: start flow failed" error="whatsapp get QR channel: GetQRChannel must be called before connecting"
time=... level=INFO msg="whatsapp: connected" jid=<accountA-jid> channel=whatsapp
time=... level=INFO msg="whatsapp: connected" jid=<accountA-jid> channel=whatsapp-B   ← same JID!

whatsmeow_device has only one row. whatsapp-B inherits account A's Store.ID, so whatsmeow.Connect() short-circuits the QR flow and GetQRChannel then refuses (the device is already paired).

Expected

Each channel_instance should own its own device row in whatsmeow_device, and a fresh instance with no prior pairing should bring up a new QR session bound to that instance.

Code path

  • internal/channels/whatsapp/whatsapp.go Channel.Start calls c.container.GetFirstDevice(ctx).
  • internal/channels/whatsapp/auth.go Channel.StartQRFlow and Channel.Reauth likewise.
  • cmd/gateway.go registers a single whatsapp.FactoryWithDBAudio(...) shared by all WhatsApp instances; the factory has no notion of which instance it is constructing for, so it cannot scope the device lookup.

Fix proposal

Persist the paired JID on channel_instances.config (key "jid") and resolve the device per channel:

  1. Read config.jid in the factory; pass it + ChannelInstanceStore into whatsapp.New.
  2. New helper Channel.resolveDevice(ctx):
    • If config.jid set → container.GetDevice(ctx, jid).
    • Else if exactly one device + exactly one WhatsApp instance exist → adopt that device (one-shot upgrade path for existing single-instance deploys).
    • Else → container.NewDevice() so the QR flow runs.
  3. On events.PairSuccess (and on adoption) write the new JID back to channel_instances.config.
  4. InstanceLoader calls a new optional SetInstanceID(uuid.UUID) hook so the WhatsApp channel knows its own row id for the writeback.

PR with the implementation + tested deploy: #1065 (link follows).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1-highData loss, core logic broken — fix in sprintarea:channelsTelegram, Discord, channel managerbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions