Skip to content

fix(session): make eager status backfill opt-in - #1040

Open
duckvhuynh wants to merge 1 commit into
rmyndharis:mainfrom
duckvhuynh:fix/disable-eager-status-backfill
Open

fix(session): make eager status backfill opt-in#1040
duckvhuynh wants to merge 1 commit into
rmyndharis:mainfrom
duckvhuynh:fix/disable-eager-status-backfill

Conversation

@duckvhuynh

Copy link
Copy Markdown

Summary

This PR makes the eager WhatsApp Status history backfill on session ready opt-in instead of unconditional.

On an affected freshly paired whatsapp-web.js account, OpenWA reached READY successfully but WhatsApp revoked the companion at the first scheduled WhatsApp Web reload, approximately five minutes later. After isolating OpenWA-specific work performed during ready, the trigger was narrowed to the immediate status@broadcast history read used to seed the Status store.

Disabling only that read allowed the same account, browser, container, Web build, and production configuration to survive the reload and remain connected. Live Status events continue to work; only the one-time backfill of statuses that predate the connection is disabled by default.

Related to #982. This is a follow-up to the onboarding-modal handling in #998: auto-dismissing the modal improved onboarding visibility but did not stop the deterministic logout for this account.

Problem

Observed behavior

For the affected account, every fresh pairing followed the same sequence:

  1. QR pairing succeeds.
  2. whatsapp-web.js emits authenticated and ready.
  3. OpenWA reports the session as READY and the page is usable.
  4. At almost exactly five minutes, WhatsApp Web performs its normal scheduled reload.
  5. The page briefly returns to CONNECTED with hasSynced=true and a valid WWebJS runtime.
  6. Within roughly 0.4–1.2 seconds, the page navigates to ?post_logout=1&logout_reason=0.
  7. whatsapp-web.js emits disconnected: LOGOUT, removes the LocalAuth profile, and OpenWA can only return to a fresh QR.
  8. The companion disappears from Phone > Linked devices without a phone-side logout notification.

The result is a deterministic loss of credentials after an apparently successful link. Container restarts, reconnect retries, and persistent volumes cannot recover the session because whatsapp-web.js deletes the credentials after the WhatsApp-side logout.

Investigation and root-cause isolation

The following possible causes were tested and did not change the outcome:

  • latest/unpinned WhatsApp Web and an explicitly pinned Web version;
  • onboarding modal auto-dismiss enabled and disabled;
  • Linux and Windows user agents;
  • headless and headful Chromium;
  • Docker/Linux execution, a non-root openwa OS user, and the production Chromium flags;
  • OpenWA's ready reconciliation versus the original engine ready event;
  • browser navigation/reload handling itself.

Two controls were important:

  • A minimal whatsapp-web.js client, run inside the same production container with the same Chrome binary, OS user, account, and WhatsApp Web build, survived its five-minute reload.
  • A standalone Windows whatsapp-web.js client also survived the same reload and stayed authenticated.

The relevant behavioral difference was in SessionService.handleEngineReady(). OpenWA immediately ran:

handleEngineReady()
  -> seedStatuses()
  -> engine.getChatHistory('status@broadcast', 50, true, ...)

That eager history fetch happens after the new companion becomes ready but before its first scheduled Web reload.

Single-variable A/B test

The production-equivalent test was repeated with only the eager Status seed changed:

Variant Status history read on ready Result at first ~5-minute reload
Existing behavior Enabled WhatsApp navigated to post_logout=1, emitted LOGOUT, removed the linked device, and deleted LocalAuth
Proposed default Disabled Web runtime returned to CONNECTED/hasSynced; no logout occurred and credentials remained present

Successful run timeline:

Time Observation
10:49:10 Freshly paired session reached READY
10:54:10 Scheduled WhatsApp Web reload began
10:54:12 Runtime returned as CONNECTED, hasSynced=true, with WWebJS available
10:59:49+ Session was still READY; no post_logout, no LOGOUT, and the profile remained intact

This does not imply that every WhatsApp account is rejected by the history request. It establishes that, for affected accounts, the immediate status@broadcast fetch is the causal OpenWA-side trigger. Because the backfill is optional enrichment rather than a session-readiness requirement, making it opt-in is the safer default.

Changes

Session behavior

  • Add STATUS_SEED_ON_READY parsing with the accepted truthy values true, 1, yes, and on.
  • Do not call seedStatuses() when the variable is absent or false.
  • Emit a structured debug log with action status_seed_on_ready_disabled when the backfill is skipped.
  • Preserve the existing asynchronous seed behavior unchanged when operators explicitly opt in.

Configuration

  • Document STATUS_SEED_ON_READY=false in .env.example.
  • Forward it from docker-compose.yml, defaulting to false.

Documentation

  • Add a troubleshooting entry for the characteristic five-minute reload/logout sequence.
  • Explain the tradeoff and how to opt back in.

Tests

  • Verify that Status history is not fetched on ready by default.
  • Keep all existing seed behavior covered by enabling the new option in those tests.
  • Clear the process environment before each test to prevent cross-test leakage.

Compatibility and tradeoffs

  • Live Status delivery is unchanged. New Status posts still arrive through the normal live message/event path.
  • Default behavior changes only for historical Status seeding. Active statuses posted before the session connected are not backfilled unless explicitly enabled.
  • Operators whose accounts tolerate the request and who require the historical snapshot can set:
STATUS_SEED_ON_READY=true
  • No database migration, API change, session re-pairing, or credential format change is required.
  • Baileys behavior is not otherwise changed; the guarded call is in the shared session service, and the backfill remains available only by explicit opt-in.

Validation

Automated checks on this branch

  • npx eslint src/modules/session/session.service.ts src/modules/session/session.service.spec.ts — passed.
  • npm test -- --runInBand src/modules/session/session.service.spec.ts236/236 passed.
  • Focused default/opt-in Status tests — 6/6 passed.
  • docker compose -f docker-compose.yml config --quiet — passed.
  • git diff --cached --check — passed (only local Windows LF/CRLF conversion notices).

Production-equivalent runtime checks

  • Built and ran the production Docker setup with PostgreSQL, Redis, MinIO, persisted session data, headless Chromium, and the same Puppeteer flags used by the deployment.
  • Confirmed the fresh-pair A/B result described above.
  • After syncing to OpenWA v0.12.2 and rebuilding, the existing linked profile restored automatically and reached READY in about four seconds without a new QR.
  • Confirmed the status_seed_on_ready_disabled debug event.
  • Confirmed the restored session remained connected beyond six minutes with the credential profile intact.

Existing upstream build issue observed locally

npm run build currently stops at an unrelated TypeScript error in src/engine/adapters/whatsapp-web-js.adapter.ts:523, where msg.downloadMedia() can resolve undefined but resolveBounded accepts only MessageMedia | null. That file is byte-for-byte unchanged from upstream/main in this branch and is outside this PR's diff. The complete SessionService test suite and lint checks for the changed TypeScript files pass.

Rollout

No action is required for affected installations after updating: the safer behavior is the default. Operators who intentionally depend on pre-connection Status history should enable STATUS_SEED_ON_READY=true after validating that their accounts remain linked through the first scheduled WhatsApp Web reload.

@duckvhuynh
duckvhuynh marked this pull request as ready for review August 2, 2026 01:52
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.

1 participant