Skip to content

fix: preserve WhatsApp sessions across 408/515 reconnects and stale WA version - #55

Merged
Neup123 merged 13 commits into
mainfrom
fix/connection-stability-bundle
Nov 10, 2026
Merged

Neup123 merged 13 commits into
mainfrom
fix/connection-stability-bundle

Conversation

@Neup123

@Neup123 Neup123 commented Nov 10, 2026

Copy link
Copy Markdown
Owner

Summary (refs #54)

Base: main at f1ccc8f3a0aabf62b4540dfc4cd87812d03a5d94 (includes merged PR #53). Baileys remains pinned 7.0.0-rc14. No release, merge, or deployment.

Scope Change Provenance
1. Keep healthy sessions alive 408 is terminal only for creds.registered !== true; registered 408 reconnects. Existing retry machinery now uses 0..min(60s, 1s·2^n) full jitter, 515 immediate, reset on open. Previous socket listeners are removed and old socket ended/closed before a new makeWASocket. Direct send/relay paths and dynamic relay invoke are bounded by withSendTimeout (60s default, EVOLUTION_SEND_TIMEOUT_MS override). FAFAzin/VENDORA discussion of Evolution #2501, deliberately not its unsafe unconditional 408 exclusion.
2. 515→401 Record CB:stream:error 515; a trailing 401 within 30s reconnects rather than triggering logout/credential cleanup. An old or independent 401 remains terminal. Evolution PR #2509 / issue evolution-foundation#2498.
3. Zombie removal Catch socket logout exceptions inside deleteInstance() and continue to remove.instance so cleanup is not stranded. Evolution PR #2520 (merged upstream develop, absent from this fork).
4. WA version cache Extend the existing single patches/baileys+7.0.0-rc14.patch with isolated evolution-foundation#2777 version-resolver hunks and typed lib/Utils/wa-version files; route socket creation through its resolver. Persist last-known-good version at instances/wa-web-version.json on the deployed volume (override EVOLUTION_WA_VERSION_CACHE_PATH). Failing/stale fetches cannot downgrade. The original #53 companion_reg_refresh, evolution-foundation#2602 and evolution-foundation#2749-line hunks are preserved unchanged. chaeulso/baileys BUGFIXES, Baileys #2777.

Intentional exclusions

Verification status — DO NOT MERGE until runner checks complete

  • GitHub connector static audits: (a) branch is based on the exact fix(baileys): restore rc14 QR companion re-pairing after production LOGOUT #53 merge; (b) single Baileys patch retains fix(baileys): restore rc14 QR companion re-pairing after production LOGOUT #53 QR marker; (c) the regenerated patch hunk counts are structurally valid; (d) the source includes guards/jitter/timeout/cleanup; (e) four explicit regression scenarios added to test/connection-recovery.test.ts. These are static, not execution passes.
  • Clean npm ci / patch-package --error-on-fail: NOT RUN in this environment (shell has no GitHub/npm registry DNS access). Must show baileys@7.0.0-rc14 success in an enabled runner.
  • npm run build: NOT RUN locally for the same reason. Await actual CI job status; do not infer success.
  • Compiled output greps: NOT RUN — no dist or installed node_modules available. Source and patch scans are not equivalent.
  • Fork suites: npm run test:connection-recovery, npx tsx test/socket-watchdog.test.ts, npx tsx test/disconnection-fields.test.ts — NOT RUN locally; ensure all three execute in a dependency-equipped environment. Socket-watchdog/disconnection tests currently have no dedicated npm scripts.
  • CI: actual results to be reported in a follow-up PR comment after GitHub exposes checks; no green claim before that.

See build plan for exact commands, incident rationale, upstream removal criteria and deployment-volume behavior. Keep this PR draft, do not merge or deploy.

Neup123 commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner Author

Verification report — current head f501ba7a117c90d5b65d70b1f3f4c5c3fe6e29e1

Status: draft / not yet runtime-verified. No merge or deployment. Repo base was independently checked as f1ccc8f3a0aabf62b4540dfc4cd87812d03a5d94 (#53 merge).

Required check Actual result / evidence
1. Clean npm ci + patch-package --error-on-fail NOT RUN. This chat's shell cannot clone the repo or reach npm: git ls-remote https://github.com/Neup123/evolution-api.git HEAD → Could not resolve host: github.com; curl https://registry.npmjs.org/... → Could not resolve host: registry.npmjs.org. No baileys@7.0.0-rc14 success line can honestly be supplied. Patch file audit below is not application evidence.
2. npm run build NOT RUN; no installed dependencies or checkout. No green claim.
3. PR CI NO CHECKS REPORTED for this head: GitHub Actions commit workflow runs [], combined status contexts [], GitHub check-runs API {"total_count":0,"check_runs":[]}. The repository contains a pull_request code-quality workflow, but none has reported on this head. This is not a pass.
4. Compiled dist/patched node_modules greps NOT RUN; there is no built dist nor installed package in this shell. Static source/patch markers found: 408 registration guard, full-jitter Math.random() * ceilingMs, withSendTimeout, 515 30s timestamp, remove.instance, version readVersionCache/writeVersionCache, and unchanged #53 companion_reg_refresh. These are not compiled-output checks.
5a. npm run test:connection-recovery NOT RUN (dependencies unavailable); four regression cases added for registered/unregistered 408 and recent/expired 515→401. After review, the existing connection-recovery and socket-watchdog fixtures were also corrected to mark their simulated healthy sockets as registered; otherwise their 408 probes would incorrectly take the new unregistered terminal path. Not a test pass.
5b. npx tsx test/socket-watchdog.test.ts NOT RUN (dependencies unavailable). No package.json script for this existing suite.
5c. npx tsx test/disconnection-fields.test.ts NOT RUN (dependencies unavailable). No package.json script for this existing suite.

Static patch/source audit output

{
  "basePatchPrefixPreserved": true,
  "patchNewFeatures": {"monotonic": true, "diskCache": true, "QR": true},
  "source": {
    "registered408": true, "jitter": true, "immediate515": true,
    "sendTimeout": true, "sendWrappers": true, "last515": true,
    "oldSocketEnded": true, "zombieEmit": true
  },
  "patchHunks": 14,
  "invalidHunkCounts": 0,
  "genericsJSHunksMatchChaeulsoPreFixBaseline": [true, true],
  "genericsDTSHunkMatchesChaeulsoPreFixBaseline": [true]
}

The patch preimage comparisons above are to the publicly available chaeulso pre-evolution-foundation#2777 baseline, not an actual node_modules/baileys@7.0.0-rc14 installation. An operator must confirm npm ci applies this exact consolidated patch in the real pinned npm package. A failed apply is blocking, with postinstall still configured --error-on-fail.

Provenance/deviations: (1) FAFAzin/VENDORA per Evolution evolution-foundation#2501 discussion — copied no blanket-408 or logout-reliability code; (2) Evolution evolution-foundation#2509 30s guard; (3) Evolution evolution-foundation#2520 zombie cleanup only (upstream merged but absent in fork); (4) chaeulso evolution-foundation#2777 monotonic cached resolver. evolution-foundation#2784 intentionally omitted because it changes unrelated init-query and pre-key retry machinery and cannot be included as isolated low-risk hunks without execution verification. FAFAzin's two specifically named public doc paths were unavailable at their default-branch locations. The existing QR patch bytes remain the prefix of the consolidated patch.

See docs/build-plan-connection-stability.md for commands and removal conditions. Blocking before merge: run clean install, build, all three suites and compiled greps with captured output, and verify actual CI status.

Neup123 commented Nov 10, 2026

Copy link
Copy Markdown
Owner Author

Verification round 1 follow-up — jitter-aware recovery test fix

Coordinator round 1 on previous head f501ba7a117c90d5b65d70b1f3f4c5c3fe6e29e1 (node:24-alpine): clean npm ci successfully applied baileys@7.0.0-rc14 with patch-package --error-on-fail on host and container. Installed Baileys contained both #53 (companion_reg_refresh) and evolution-foundation#2777 (resolveWaWebVersion, readVersionCache, writeVersionCache) fixes. Prisma generate and tsc --noEmit passed. test/disconnection-fields.test.ts and test/socket-watchdog.test.ts passed. Full-diff review found items 1–4 aligned and accepted evolution-foundation#2784 exclusion.

One failing test in round 1: test/connection-recovery.test.ts case failedReconnectThenOpen at old line 88: the 5-ms polling assertion could see a fully recovered open after full-jitter scheduled two retries at near-zero delay. The corresponding base-main suite passed in the identical rig. The existing test wrongly relied on the former fixed 5/10-ms backoff, not a production regression.

Fix pushed in commit e8882eb4 on the same fix/connection-stability-bundle branch:

  • failedReconnectThenOpen: record stored.connectionStatus when the recovery connectToWhatsapp call begins (after the first attempt threw), and assert the captured value is close after successful recovery. This checks the same invariant at a meaningful lifecycle boundary rather than 5-ms wall-clock polling.
  • hangingReconnectThenOpen: analogously capture and assert close at the retry boundary after the first attempt timed out.
  • Preserve assertions for close immediately after initial transition, eventual open, at least 2 attempts, and outcome=threw/outcome=opened/outcome=timed_out logs.
  • No production changes, no Math.random pinning, no minimum delay, no change to reconnect scheduling. Compared to f501ba7a, the commit modifies only one test file (+9/−2).

Current verification status

  • Pass from coordinator's previous-head run: clean patch application, installed QR+version hunks, Prisma generate, tsc --noEmit, disconnection-fields, socket-watchdog.
  • Awaiting fresh coordinator execution on new head e8882eb4: all 8 cases in npm run test:connection-recovery. I could not run the repository suites locally: the chat execution container has no npm registry DNS access or installed repo dependencies. This is a static test logic fix, not an independently verified passing test result.
  • Build: coordinator reports npm run build exhausted the 2 GiB container heap; this is a rig resource limitation, not a code change requested here. Production Docker build with a raised heap is handled outside this PR.
  • PR checks: GitHub check-runs API currently reports total_count: 0 for new head, so do not report CI green.

Keep PR draft / do not merge or deploy as part of this task.

Neup123 commented Nov 10, 2026

Copy link
Copy Markdown
Owner Author

Verification round 2 follow-up — missing class-field initialization in test harness

New head: e6cd80b656f80b24ca5ab401b8927cffb6f80254, on the same draft PR #55 / branch fix/connection-stability-bundle.

Coordinator round 2 on previous head e8882eb4 (node:24-alpine, raised heap):

  • Clean npm ci: PASS, patch-package applied consolidated baileys@7.0.0-rc14 patch.
  • Prisma generate: PASS.
  • npm run build with NODE_OPTIONS heap 8192 MiB: PASS, reported "Build success".
  • Compiled dist: confirmed lastStream515At, stream515GraceMs, WhatsApp send timed out, Zombie socket logout failed.
  • test/disconnection-fields.test.ts: PASS.
  • test/socket-watchdog.test.ts: PASS.
  • test/connection-recovery.test.ts: prior failedReconnectThenOpen and hangingReconnectThenOpen cases PASS after jitter-aware snapshots. New trailing401After515Retries case FAILED (expected scheduleReconnect([401]), actual []). Suite aborted before expired515DoesNotMaskLogout.

Root cause: the test uses Object.create(BaileysStartupService.prototype), bypassing constructor-initialized fields. It sets lastStream515At but omitted stream515GraceMs; the comparison against undefined (NaN) was false. Production construction does initialize stream515GraceMs = 30_000.

Fix (one test-only line): set service.stream515GraceMs = 30_000; in the common connection recovery harness alongside reconnectBackoffBaseMs. No production files, Baileys patch, reconnect scheduling, or other test suites touched. Compare to previous head: one file, +1/−0.

Current status: This fix directly corrects the test-harness cause, including the 30-second boundary used by trailing401After515Retries and expired515DoesNotMaskLogout (31 seconds), but the full eight-case suite has NOT been rerun on new head. The previously passing two suites and build apply to preceding head; do not claim new-head runtime success without a rerun. GitHub reports 0 check runs and 0 PR-triggered workflow runs for the new head. No merge/deploy.

@Neup123

Neup123 commented Nov 10, 2026

Copy link
Copy Markdown
Owner Author

Coordinator verification — connection-stability bundle (PR #55, head e6cd80b6)

Verified independently on a network-enabled machine (home PC + Linux container rig, node:24-alpine), three rounds. Not merged, not deployed.

Delivered scope (full diff read by the coordinator)

Evidence

  • Clean npm ci applies the regenerated patch — patch-package success line for baileys@7.0.0-rc14, --error-on-fail silent (proven 5×: host + container installs). Installed Baileys contains both fix sets: companion_reg_refresh handler + companion-reg-client-utils.js (fix(baileys): restore rc14 QR companion re-pairing after production LOGOUT #53), wa-version.js with resolveWaWebVersion / readVersionCache / writeVersionCache and the generics.js re-export (feat(baileys): add sendPollVote and sendEventResponse endpoints evolution-foundation/evolution-api#2777).
  • Build green: tsc --noEmit clean; npm run build green with a raised heap (the stock build OOMs at this machine's default heap — pre-existing rig limit, same as fix(baileys): restore rc14 QR companion re-pairing after production LOGOUT #53); production Docker image builds green (wrapper Dockerfile = repo Dockerfile + one builder-stage ENV NODE_OPTIONS=--max-old-space-size=8192 line; repo Dockerfile untouched).
  • Suites (Linux container): connection-recovery ✅ (all 8 cases, incl. the 4 new ones: registered 408 retries, unregistered 408 terminates, 401 within the 515 grace retries, 401 at 31s does not), disconnection-fields ✅, socket-watchdog ✅. Control: the connection-recovery suite passes on base f1ccc8f3 in the identical rig. Two test-harness defects found during verification were fixed test-only (jitter-aware staging; harness now sets stream515GraceMs explicitly) — production code unchanged by both fixes.
  • Compiled output / shipped image greps: lastStream515At, stream515GraceMs, EVOLUTION_SEND_TIMEOUT_MS, WhatsApp send timed out, Zombie socket logout failed, Superseded WhatsApp socket all present in dist and inside the published image; the image's Baileys socket.js carries the companion handler (count 2) and wa-version.js ships (11,633 bytes).
  • CI: this repository reports no checks (GitHub Actions is disabled for the repo) — stated plainly, not claimed green.

Publication

ghcr.io/neup123/evolution-api:latest → digest sha256:357879575166973895a0ed82841282b4837cc710017f1847b64b55d90d34fa4d, built and pushed from the home PC at head e6cd80b6 (labels: revision e6cd80b6, version 5.5.3), cross-checked via the GHCR API.

Merge and Coolify deployment (digest-pinned, per the known :latest redeploy trap) are the parent's steps.

@Neup123
Neup123 marked this pull request as ready for review November 10, 2026 20:31
@Neup123
Neup123 merged commit 1eecf84 into main Nov 10, 2026
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