Why Arabic looks broken in your Facebook data export, and the one-line fix
Meta's JSON export writes every byte of an Arabic letter as a separate escape. Any JSON reader turns that into Latin characters, and nothing reports an error.
By Firas Mohammed · · 3 min read
The short answer
Meta's JSON export writes every UTF-8 byte of a non-English character as its own \u00XX escape. A JSON reader turns each escape into one Latin-1 character, so the Arabic "ال" comes out as "ال". The bytes are all there. Parse the file, then encode each string as Latin-1 and decode the result as UTF-8. In Python that's one line: text.encode('latin-1').decode('utf-8').
Where I ran into it
I was building one searchable archive from seven export formats: about 570,000 messages from 2009 to 2026, roughly half of them in Arabic. One platform served UTF-8 text labelled as Latin-1. The import looked clean. Message counts were right, English survived, and nothing raised an error. Every Arabic character had been replaced.
If I'd trusted that clean run, half the corpus would have been destroyed while the pipeline reported success.
What happens, byte by byte
Take the letter ا (alif). It's U+0627, and in UTF-8 it's two bytes: D8 A7. Meta writes it as \u00d8\u00a7. A JSON parser reads that as two characters: Ø (U+00D8) and § (U+00A7).
ل (lam) is D9 84. That becomes Ù plus U+0084, an invisible control character. Windows tools that read text as cp1252 draw that byte as „, which is how "ال" turns into "ال" on screen.
Nothing complains because every byte from 00 to FF is a valid Latin-1 character. English letters are single bytes that mean the same thing in both encodings, so they pass through untouched.
A quick tell: every letter in the main Arabic Unicode block (U+0600 to U+06FF) starts with byte D8, D9, DA or DB. Broken Arabic is full of Ø, Ù, Ú and Û.
The fix, tested
This walks the whole file, fixes every string including sender names, and writes a readable copy:
import json
def fix(x):
if isinstance(x, str):
try:
return x.encode('latin-1').decode('utf-8')
except (UnicodeEncodeError, UnicodeDecodeError):
return x # already correct, leave it
if isinstance(x, list):
return [fix(v) for v in x]
if isinstance(x, dict):
return {fix(k): fix(v) for k, v in x.items()}
return x
with open('message_1.json', encoding='utf-8') as f:
data = fix(json.load(f))
with open('message_1.fixed.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
I tested it on a file I built to match Meta's escaping, with Arabic, an emoji and the word "café". All 4 messages came back identical to the originals. A second run on the fixed file changed nothing, because correct text fails the UTF-8 decode and the except leaves it alone.
Use latin-1 for the encode step. With cp1252 it fails on ف (U+0641), whose second byte is 0x81, a value cp1252 leaves undefined.
In Node.js it's one line, with raw holding the file's text:
const data = JSON.parse(raw, (k, v) => typeof v === 'string' ? Buffer.from(v, 'latin1').toString('utf8') : v);
On text that's already correct, Node mangles it without an error. In my test "مرحبا" came back as E1-('. Run it once, on the original download.
Check the result
- Search the fixed file for Ø and Ù. In Arabic-heavy text you should find close to none.
- Open a few Arabic messages you remember and read them through.
- Look at the sender names. They're strings in the same file and break the same way.
- Find a message with an emoji. Each one is 4 bytes in the export and should come back as 1 character.
- Keep
ensure_ascii=Falsewhen you save. Without it Python writes escapes like\u0645, which are correct and hard to read by eye.
Questions people ask
Is my Facebook data corrupted?
The bytes are all there. Fix it from the original JSON file and every character comes back. If another tool already saved the garbled text in a different encoding, go back to the original download.
Does Instagram's export have the same problem?
Yes. Instagram's JSON download uses the same \u00XX escaping, and the same code fixes it.
Does this only affect Arabic?
Every character outside basic English breaks the same way, including accented letters, Cyrillic, Hindi and emoji. My test file had "café" and an emoji, and both came back.
Why doesn't my JSON viewer show an error?
The file is valid JSON and every escape is a legal character. The mistake happened when the file was written, and a parser has no way to see it.
The project behind this
This bug came out of a larger pipeline that normalised seven export formats into one message schema with around 40 small Python programs. The full story is in the personal archive case study.
If you have Arabic exports or archives that need cleaning into something searchable, this is work I take on.