This is one of the sneakiest bugs I've ever run into in WordPress, and it took me more than four days to figure out what was causing it. On an editorial site I built, a guide page stored its list of chapters as JSON in post meta. One day the list just stopped showing up — no error, no white screen, just "0 items" even though the data was clearly still sitting in the database.
The maddening part: the row size in the database stayed the same (the data was still there, roughly that size), but json_decode() returned null. So the data appeared to be present but unreadable.
The cause made me want to bang my head on the desk: update_post_meta strips the backslashes out of your JSON.
Why this happens
update_post_meta() (and its siblings) runs wp_unslash() on the value internally before saving. For ordinary data this is harmless. But wp_json_encode() produces a string that depends on backslashes:
\n(escaped newline) →wp_unslashturns it inton\"(escaped quote) → becomes"— which immediately breaks the JSON structure\t,\/,\uXXXX→ all corrupted along the way
The result: the stored string is no longer valid JSON. json_decode gives up and returns null, so your template renders zero items. And because the data size barely changes, you'll never suspect "data got truncated."
Here's how to confirm it — compare what you send versus what gets stored:
$data = [ 'judul' => 'Bab "Satu"', 'isi' => "baris1\nbaris2" ];
update_post_meta( $pid, 'daftar_bab', wp_json_encode( $data ) );
$tersimpan = get_post_meta( $pid, 'daftar_bab', true );
var_dump( json_decode( $tersimpan, true ) ); // null ← brokenThe fix: one line
Wrap the wp_json_encode output in wp_slash() before saving. This "adds" the extra backslashes that the internal wp_unslash will later strip — so your JSON reaches the database intact:
update_post_meta( $pid, 'daftar_bab', wp_slash( wp_json_encode( $data ) ) );That's it. wp_slash and the internal wp_unslash cancel each other out, and your JSON gets stored exactly as you built it.
What matters: it's not just update_post_meta
This is the same trap in several WordPress functions that run wp_unslash internally. Commit this list to memory:
| Function | Hit by the slash bug? |
|---|---|
update_post_meta / add_post_meta | Yes — needs wp_slash |
update_term_meta / update_user_meta | Yes — needs wp_slash |
update_option | Yes — needs wp_slash |
wp_insert_post (for post_content) | No — different code path, don't slash it |
set_theme_mod / get_theme_mod | No — safe without slashing |
Pay attention to the last two rows: if you also add wp_slash to post_content or theme_mod, you create the opposite problem — extra backslashes leaking into your output. So the rule isn't "always slash," it's "slash only the functions that unslash."
The takeaway
php -l passes. There's nothing in the logs. The data is "there." But it's been quietly corrupted for days because WordPress mangled your value behind the scenes. If you're storing JSON (or anything that depends on backslashes) via the metadata/option API, always wp_slash the output of wp_json_encode — and verify with json_decode after saving, not just before.
