WhatsApp for macOS said it was using 5.7 GB. Its data folder was 158 GB.
This repo contains the script I used to find and remove the difference: 444,018 orphaned media files taking up 151 GB, none of which were referenced by any message.
WhatsApp → Settings → Storage and Usage reports a sensible number. Meanwhile:
du -sh ~/Library/Group\ Containers/group.net.whatsapp.WhatsApp.shared
158G
The built-in "Manage Storage" screen is no help, because it only lists media it still has database records for. The orphans are invisible to it.
ChatStorage.sqlite records the on-disk path of every piece of media, in
ZWAMEDIAITEM.ZMEDIALOCALPATH (full media) and ZWAMEDIAITEM.ZXMPPTHUMBPATH (thumbnails).
Comparing that set against what is actually in Message/Media/:
| files | size | |
|---|---|---|
| Referenced by a message | 27,481 | 5.67 GB |
| Referenced by nothing | 444,018 | 151.3 GB |
Every referenced file was present — nothing was missing or broken. The 151 GB was pure surplus.
The surplus turned out to be exact duplicates. In the worst chat:
| Files on disk | 126,504 |
| Distinct images | 21 |
| Copies of each | 3,012 |
| Unique content | 20.3 MB |
| Space consumed | 59.7 GB |
Verified with md5 — byte-for-byte identical, not merely the same size.
The severe duplication was confined to one-to-one chats, whose media folders are named with
WhatsApp's newer "linked identity" format, <id>@lid. Group chats, named <id>@g.us, carried
only trivial amounts — a factor of roughly 1.1, versus up to 4,518.
| chat | folder suffix | files | distinct files | factor |
|---|---|---|---|---|
| A | @lid |
126,504 | 28 | 4,518× |
| B | @lid |
84,336 | 21 | 4,016× |
| C | @lid |
98,943 | 55 | 1,799× |
| D | @g.us |
3,230 | ~2,960 | ~1.1× |
| E | @g.us |
1,862 | ~1,710 | ~1.1× |
("distinct files" counts unique file-type-and-size combinations across all media in the chat; the 21-images figure above counts unique JPEGs only, which is why the factors differ.)
That pattern points at the migration from phone-number identifiers
(<number>@s.whatsapp.net) to @lid: media looks to have been re-downloaded into fresh
paths on repeated syncs without the previous copies being cleaned up. The database backs
this up — it still holds a mixture of both path formats.
I have not seen WhatsApp's source, so treat the mechanism as a well-supported hypothesis rather than a confirmed root cause. The measurements themselves are reproducible with the script below.
Requires macOS, python3 and sqlite3 — both ship with the OS.
Quit WhatsApp first, then:
./whatsapp-orphan-cleanup.sh # dry run, reports what it would delete
./whatsapp-orphan-cleanup.sh --delete # actually deleteReopen WhatsApp afterwards and scroll a few photo-heavy chats to confirm media still loads.
The script is dry-run by default and refuses to proceed if:
- WhatsApp is running
ChatStorage.sqlitefailspragma integrity_check- fewer than 100 files come back as referenced (a sign the query returned nonsense)
- any referenced file is already missing from disk
Before deleting anything it copies ChatStorage.sqlite to
~/Documents/whatsapp-db-backup/. It snapshots the database together with its -wal and
-shm sidecars, so the write-ahead log is replayed and recently received media is counted
as referenced — reading the .sqlite file alone gives a stale answer.
Nothing visible in the app is lost: media with no database row cannot be displayed by WhatsApp regardless.
The underlying bug will likely start rebuilding the pile, so it is worth re-running occasionally.
On the machine this was written for, the script removed 444,018 files and freed 151 GB. Re-scanning the worst-affected chats afterwards showed 42, 28 and 43 files remaining, where they had held 126,504, 84,336 and 98,943 — a duplication factor of 1× throughout. Message history and visible media were unaffected.
Written against WhatsApp for macOS in 2026 and the schema it ships today. It deletes files permanently. Read it before you run it, and keep your own backup.
MIT