cd /news/developer-tools/whatsapp-business-for-ios-crashes-af… · home topics developer-tools article
[ARTICLE · art-49624] src=granot.io ↗ pub= topic=developer-tools verified=true sentiment=↓ negative

WhatsApp Business for iOS Crashes After 16 Seconds When Companion Is Connected

WhatsApp Business for iOS crashes 13 to 46 seconds after launch when a companion device is linked, due to an unbounded receipt-tracking table with 872,787 rows that causes the app to exceed the 3,376 MB per-process memory limit. The issue affects versions 26.22.76 through 26.24.73 on iOS 26.5.1, and logging out all companion devices restores stability.

read10 min views1 publishedJul 7, 2026
WhatsApp Business for iOS Crashes After 16 Seconds When Companion Is Connected
Image: source

This is a writeup, mostly generated by AI and follows the writing style of good old MSDN forums and announcements.

I've been blocked from using WhatsApp web for weeks now because of this, so I decided to release the writeup that somewhat unblocked me, if someone else is facing this too.

It has been lightly edited, but mostly is the result of two independent investigation sessions conducted using Fable 5.

Summary #

WhatsApp Business for iOS on my device is killed by the operating system 13 to 46 seconds after every launch, but only while a companion device (WhatsApp Web or Desktop) is linked to the account. With no linked devices, the application is stable indefinitely.

Over two investigations spanning June and July 2026, three distinct defects were identified in the application's local data, two of which were confirmed and eliminated.

The third and currently active cause is an unbounded per-device receipt-tracking table (receipt_device

in MessagingInfraDatabase.sqlite

) containing 872,787 rows. Evidence indicates that a launch-time reconciliation routine, active only when a companion device is registered, materializes this table in memory in full.

At approximately 4 KB per materialized row, the resulting allocation reaches the 3,376 MB per-process memory limit, and iOS terminates the process.

The failure is not caused by database corruption, media volume, or account state on WhatsApp's servers. All analysis was performed read-only against decrypted copies of encrypted local iPhone backups. No jailbreak was used and no data was modified on the device.

Environment #

Item Value
Device iPhone18,3
OS iOS 26.5.1 (build 23F81)
Application WhatsApp Business (net.whatsapp.WhatsAppSMB ), versions 26.22.76 through 26.24.73
Account scale ~1.95M messages, ~6,700 chats, ~960k media item records
Primary database ChatStorage.sqlite , 1.6 GB
Analysis host macOS, libimobiledevice, iphone_backup_decrypt , apsw (SQLite)

Symptom #

When a companion device is linked, the phone application exits to the home screen shortly after launch. The interval between launch and termination was measured at 13, 16, 25, 29, 36, and 46 seconds across six reproductions on July 7. The companion session itself remains connected throughout, because the multi-device architecture serves companions from WhatsApp's servers rather than from the phone. Logging out all companion devices restores full stability; relinking any companion reintroduces the failure on the next launch.

Two secondary kill modes were observed in the same period and are attributed to the same underlying workload:

0xDEAD10CC

terminations: the process was suspended while holding a file lock (the SQLite database in the shared app-group container).diskwrites_resource

violations: sustained SQLite write rates of 65 to 93 KB/s against an iOS budget of 12.43 KB/s, exhausting the 1,073.74 MB daily write allowance in three to four hours.

Methodology #

All conclusions derive from three data sources.

1. iOS crash and resource reports (.ips

files), retrieved with idevicecrashreport

. These identify the kill reason (jetsam per-process limit, watchdog, suspension-with-lock, disk-write budget) and, for resource violations, include microstackshot samples of the hot path.

2. Encrypted local backups, taken with idevicebackup2

and decrypted selectively with iphone_backup_decrypt

. The backup manifest allows individual files to be located and extracted by domain and path without decrypting the full 80 GB archive. Every extracted database was opened immutable to make writes impossible at the API level:

con = apsw.Connection(
    "file:ChatStorage.sqlite?mode=ro&immutable=1",
    flags=apsw.SQLITE_OPEN_READONLY | apsw.SQLITE_OPEN_URI)

3. Live syslog capture during reproduction, via idevicesyslog

, with the phone connected over USB while the failure was triggered on demand.

Finding 1 (June): a counter at INT32_MAX poisoned the history exporter #

The June failure mode was a crash during companion history transfer, with mach_vm_allocate

failures at modest process footprint and a database that passed PRAGMA integrity_check

. That combination (failed allocation, low usage, structurally healthy data) suggested the exporter was sizing a single allocation from a pathological value rather than running out of memory gradually.

A two-stage scan confirmed this. The first stage measured MAX(LENGTH(...))

for every TEXT and BLOB column in the database and ruled out oversized fields; the largest single field in 1.6 GB of data was 130 KB. The second stage took the minimum and maximum of every ID, counter, and sort column:

for c in ("Z_PK", "ZSORT", "ZMESSAGECOUNTER", ...):
    cur.execute(f"SELECT MIN({c}), MAX({c}), COUNT(*) FROM {t}")

Two anomalies emerged, both parked at 32-bit boundaries:

Field Observed value Organic maximum
ZWACHATSESSION.ZMESSAGECOUNTER (1 row)
2,147,473,649 = INT32_MAX − 9,998 162,718
ZWAMESSAGE.ZSORT (149 rows)
214,748,3xx ≈ INT32_MAX / 10 162,718

The value distribution was diagnostic: nothing existed between the organic maximum and the sentinel cluster, indicating the application itself wrote these values as overflow sentinels rather than accumulating them organically.

The poisoned counter resided on the database's only WhatsApp Channel row. Notably, unfollowing a channel does not delete its row; it sets a hidden flag (ZHIDDEN=1

) while the row and its counter remain in the database, unreachable from the UI but still visited by the exporter.

Remediation used WhatsApp's own backup-and-restore path (iCloud backup, delete, reinstall, restore), on the theory that restore regenerates local metadata rather than copying it. Post-restore verification confirmed the counter returned at a sane 162,773, the channel row was gone entirely, and no messages were lost. A companion link then completed normally in approximately 19 minutes. The restore had significant operational cost: roughly 21 GB of media re-download and a multi-day episode in which a first-launch cleanup routine attempting to unlink 200,000 files was repeatedly killed by the 20-second launch watchdog (0x8BADF00D

) before it could complete.

Finding 2 (July): the sentinel writer is still active, but sentinels were not the cause #

The failure returned within two weeks. A fresh extraction showed the June repair had held (sane counters, no channel rows), but one new ZSORT

sentinel had appeared, written on June 30 to the newest message of a small archived group. An incidental complication: the account contained two groups with identical display names, and the poisoned one was in the chat archive, so the first clearing attempt targeted the wrong group.

Deleting the affected group removed the sentinel. The crashes continued unchanged. A full value scan of the database after deletion found nothing anomalous, which eliminated ChatStorage.sqlite

as the cause of the ongoing failure and forced the investigation wider.

Finding 3 (July, active): an unbounded receipt table and a launch-time reconciliation loop #

Live capture of the termination

With syslog streaming, the kill sequence is fully visible. From application launch to termination is 16 seconds in the first captured cycle:

15:33:03  runningboardd: [app<net.whatsapp.WhatsAppSMB>:11429]
          Set jetsam priority to 100 (foreground)
15:33:19  kernel: memorystatus: WhatsApp [11429] exceeded mem limit:
          ActiveHard 3376 MB (fatal)
15:33:19  kernel: killing_specific_process pid 11429 [WhatsApp]
          (per-process-limit) 3457027KB
15:33:19  SpringBoard: Process exited:
          domain:jetsam(1) code:per-process-limit(7)

During those 16 seconds, the process read a single feature-flag preference key (aber

, in the group.net.whatsapp.WhatsAppSMB.shared

domain) 50,026 times, a rate above 3,000 reads per second. This is the signature of a per-item processing loop consulting a flag for each item while allocating memory it does not release. An annotated capture of one full launch-to-kill cycle is available here.

The controlling experiment: with all companion devices logged out and the application relaunched, the loop does not run (the same flag was read 2,514 times in several minutes of normal use) and the process remains alive indefinitely. Relinking a companion reintroduces the termination on the next launch. The workload is therefore gated exclusively on companion-device registration.

Locating the data

ChatStorage.sqlite

is one of fourteen SQLite databases in the application's container. A value scan across all fourteen found nothing pathological. A row-count scan did:

Database Size Notable content
MessagingInfraDatabase.sqlite
143 MB receipt_device : 872,787 rows (table + unique index account for the entire file); all other sync queues: 0 rows

The receipt_device

schema tracks delivery/read/played receipts per message, per participant, per participant device:

CREATE TABLE receipt_device(
  _id INTEGER PRIMARY KEY AUTOINCREMENT,
  stanza_id TEXT NOT NULL, chat_jid TEXT NOT NULL,
  user_jid TEXT NOT NULL, device_id INTEGER NOT NULL,
  send_timestamp LONG_INT, delivered_timestamp LONG_INT,
  read_timestamp LONG_INT, played_timestamp LONG_INT,
  device_version INTEGER)

The rows span April 20 to July 7 (79 days), an average inflow of roughly 11,000 rows per day. Six high-traffic groups account for more than half the volume. No pruning behavior is evident in the data.

The allocation arithmetic

The memory limit divided by the row count yields the per-row cost required for the table to be the allocation:

3,376 MB / 872,787 rows ≈ 4.05 KB per row

Four kilobytes per row is consistent with materializing each row as a full Objective-C object graph rather than streaming it. The observed termination sizes (3,454,851 KB to 3,458,595 KB across reproductions) are consistent with the table being loaded in full, plus baseline application footprint.

Reconstructing the table's growth from its own timestamps and plotting it against the ceiling produces the following:

The growth curve crosses the computed ceiling in the first week of July, which is when per-launch termination began.

Consistency with the secondary evidence

This mechanism accounts for the remaining observations:

  • The disk-write violations worsened from 65 KB/s (June 28) to 93 KB/s (July 2) as the table and its index grew, increasing WAL churn per operation. Stack samples in the disk-write reports show the time concentrated in direct libsqlite3

write paths. - The June restore appeared curative because it reset the container while the table held roughly 600,000 rows, below the ceiling. The table continued to grow; the failure "returned" when the row count crossed approximately 850,000. The recurrence interval was a property of the growth rate, not of the earlier fix.

  • The 0xDEAD10CC

suspension kills occur when the same long-running database workload is backgrounded mid-transaction.

Timeline #

Date Event
~Apr 20 receipt_device begins accumulating (earliest timestamps in table)
Jun 12–15 Investigation 1: INT32_MAX counter found on channel row; restore performed; companion link succeeds
Jun 16–28 diskwrites_resource violations resume and escalate
Jun 30 New ZSORT sentinel written to an archived group
Jul 2 Disk-write violation at 93.57 KB/s, daily budget exhausted in 3.2 hours
Jul 7 Investigation 2: sentinel eliminated; failure persists; live capture; companion-link gating established; receipt_device identified at 872,787 rows

Current status and workaround #

The application is fully stable with zero linked devices. That is the workaround, and it is an unsatisfying one, since linked devices are awesome.

Remaining paths, in rough order of preference:

  • Application updates, in case the reconciliation path regressed recently and is fixed upstream.
  • Determining whether an extended period with no linked devices causes the table to be pruned.
  • Reducing inflow by leaving the highest-traffic groups.
  • A report to WhatsApp with this analysis attached.
  • Repeating the container reset, which the growth data suggests buys approximately ten weeks per iteration at current inflow.
  • Banging head against wall.

Notes on reproduction #

The diagnostic queries require only a decrypted copy of the application's databases from a local encrypted backup. The two checks that matter, in order of cost:

  • Min/max of every numeric ID, counter, and sort column, flagging values near ±2³¹ and 2³¹/10. Values hugging type boundaries are written by code, not accumulated by use.
  • Row counts of every table in every database, not just the primary one. A table whose count is wildly out of proportion to the others is a candidate allocation bomb even when every individual value in it is sane.

The general lesson from the pair of investigations: a crash with clean data and a failed large allocation points to one absurd value; a crash with clean values points to an absurd count; and a fix that works and then "wears off" on a schedule is telling you the real variable is something that grows.

── more in #developer-tools 4 stories · sorted by recency
── more on @whatsapp business 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/whatsapp-business-fo…] indexed:0 read:10min 2026-07-07 ·