Serialized Data Fixer

Repair a PHP serialized value whose string lengths no longer match its contents.

Runs on your deviceWordPressWorkingNo account needed

What this tool does

WordPress keeps arrays and objects in the database as PHP serialize() output, a format that writes the byte length in front of every string — s:5:"hello". Run a plain SQL REPLACE() across the tables and the text changes while those numbers stay as they were, so unserialize() refuses the value and the row reads back as empty. That is why widgets disappear, menus empty out and theme settings return to their defaults after a domain change, even though the data is still sitting in the table exactly where you left it.

Serialized Data Fixer parses the value with those declared lengths treated as hints rather than facts. It works out where each string really ends by looking for a terminator that leaves valid data behind it, then writes the correct UTF-8 byte length back — bytes, not characters, which is what PHP counts and what catches people out on accented titles and emoji. Array and object element counts are recounted the same way, so a list that gained or lost an entry declares the right number again. Floats keep their exact printed text, reference markers are copied through untouched, and running the tool on a value that was already sound returns it unchanged and says so.

There are two outputs. Repaired serialized data is what you paste back into option_value or meta_value; readable JSON is there when you only want to see what a plugin has actually stored. The optional find and replace rewrites string values inside the parsed structure and recomputes the byte count of every string it touches, so a flat option survives a domain or upload-path swap that a SQL REPLACE() would have wrecked — with one exception, set out below, for a string that is itself serialized data. If the text came out of a mysqldump with escaped quotes and backslashes, tick the SQL dump box before running. Everything happens in this browser tab: database contents are not uploaded, and the page works with the network off.

The limits matter more than the feature list. One value per run — anything after the end of the first value is refused, so a dump or a column of rows has to be done a row at a time. Only the standard markers are understood: s, a, O, i, d, b, N and references. A class that implements Serializable (C:) or a backed enum (E:) is reported as an unrecognised value rather than repaired. Find and replace touches string values only, never array keys or class names. The JSON view is one-way — objects gain a __class__ key and references become placeholder text, so it reads well but cannot be pasted back. And where a string genuinely contains the terminator sequence, the end is chosen by scanning forward for a quote-and-semicolon whose next few characters begin like another marker, preferring the candidate whose byte count matches the declared length and otherwise taking the earliest one — a heuristic that can pick wrong.

Double serialization is the limit to check before you trust an output. Plugins and postmeta rows routinely keep one serialize() result inside another as a string, and every string here is treated as opaque text, so the nested layer is never entered. If that inner data holds a string of its own, a broken value cannot be repaired at all: the first terminator inside the nesting is taken for the end of the outer string and the run stops with a position complaint. On a nested value that is still sound, find and replace produces the very fault this page is about — the outer length is rewritten correctly while the inner s: counts keep their old totals — and running the tool again on that output reports that nothing was broken, because it cannot see inside a string. For anything nested, unserialize it in PHP or let WP-CLI search-replace walk the levels.

How to use it

  1. Paste one serialized value. Copy the whole option_value, meta_value or postmeta field, including the trailing semicolon. Load sample shows the shape it expects if you want to try it first.
  2. Say where it came from. Tick the SQL dump box when the text carries \" and \\ escapes from mysqldump, so those are removed before parsing rather than counted as content.
  3. Choose the output. Repaired serialized data to paste back, or readable JSON to inspect what the row holds without touching the database at all.
  4. Add a find and replace if you need one. Type the old domain or path and its replacement. Only string values are changed, and every affected length is recalculated afterwards.
  5. Read the summary, then copy. It reports how many string lengths and element counts were corrected. Those figures cover the lengths the parser can see, and never any held inside a nested serialized string.

Processing & privacy

Runs on your device. Your file is processed directly in this browser.

Common use cases

Widgets vanished after a domain change

A broken option makes the widget array unreadable, so the sidebar renders empty. Repairing the lengths brings the stored layout back without rebuilding it.

Customiser settings reset themselves

theme_mods_yourtheme is one serialized array. When its byte counts stop matching, the theme falls back to defaults and every colour and logo choice appears lost.

Seeing what a plugin actually stores

Switch the output to JSON and the option becomes a readable structure, which is quicker than reading a single line of s: and i: markers.

Swapping a URL inside one row

Use find and replace here instead of SQL REPLACE(). Each edited string has its length rewritten with the text, so a flat row comes back loadable — check first that no value in it is itself serialized, because those are left with stale inner counts.

An accented title broke the row

PHP counts bytes, so Café is five bytes and not four. Anything that recounted characters produces a value that will not unserialize until the byte length is restored.

Questions

Why does a search-and-replace break WordPress?
Because it edits the text without editing the numbers in front of it. PHP serialize() records each string's byte length, so shortening or lengthening the content leaves a declaration that no longer matches and unserialize() returns false.
Is my database content uploaded?
No. Parsing and repair happen in this browser tab, so nothing is transmitted or stored, and the tool keeps working with the network disconnected. That matters here because option rows regularly hold API keys and licence codes.
Can I paste a whole SQL dump or a full table?
No. The parser handles one value per run and refuses anything trailing the end of that value, naming what it found. Work row by row, or use a migration tool that unserialises each value as it goes.
Should I use this for a whole site migration?
Not on its own. For a full move, use something that unserialises every value properly — WP-CLI search-replace, or a migration plugin — and see the Migration Checklist. This tool is for the values that are already broken, or the single stubborn row.
Does it cope with accented characters and emoji?
Yes. Lengths are computed as UTF-8 bytes, matching PHP, so multibyte text is measured the way the database expects rather than by character count.
Can I turn the JSON back into serialized data?
Not here. The JSON view is for reading: object nodes are labelled with a __class__ key and references appear as placeholder text, neither of which converts back cleanly.
What if it refuses to parse my value?
The error names the position and what it expected there. The usual causes are a partial copy, a missing trailing semicolon, dump escaping that needs the SQL dump box ticked, a C:/E: marker that this parser does not implement, or serialized data nested inside a string.
Does it fix a value that was serialized twice over?
No, and this is the failure to watch for. A string whose contents are themselves serialize() output is read as plain text, so its inner lengths are neither checked nor rewritten: a broken one stops with a position error, and find and replace on a sound one leaves the nested counts stale while reporting success. Unserialize those in PHP, or let WP-CLI search-replace descend through each layer.

Related tools