Fixing WordPress Menu locations Will Not Save

Are you having issues saving your WordPress menu locations, for example you set the menu locations but when you save the dropdowns revert to please select? In my PHP errors I was getting this:

Warning Illegal string offset 'nav_menu_locations' 1
set_theme_mod()
wp-admin/nav-menus.php:403
wp-includes/theme.php:952

Here is the fix, but first let’s examine how WordPress saves theme options as it will make your future debugging easier. So wordpress saves in the wp_options table the menu locations in a serialized field. Serialized fields take strings, arrays, and objects and combine them into a giant string so that it can be saved in the database.

Here is what that looks like:

a:4:{i:0;b:0;s:18:"nav_menu_locations";a:5:{s:8:"top-menu";i:1335;s:7:"primary";i:1334;s:4:"main";i:6774;s:3:"top";i:6775;s:5:"small";i:6774;}s:19:"ot_set_google_fonts";a:0:{}s:18:"custom_css_post_id";i:-1;}

and when unserialized:

array (
0 => false,
'nav_menu_locations' =>
array (
'top-menu' => 1335,
'primary' => 1334,
'main' => 6774,
'top' => 6775,
'small' => 6774,
),
'ot_set_google_fonts' =>
array (
),
'custom_css_post_id' => -1,
)

So the issue when your menu is not saving is probably that the serialized field has become corrupted somehow. To find the error it helps to first know the very basic structure which this post describes well

After that you can try and manually unserialize this field to find the error. This can be done by first finding in your database the theme options field, so go to your wp_options stable and search for the field like “theme_mods_themename” replace “themename” with the name of the theme you are using.

After that you should get the serialized field, copy and paste this field:

Now run that through php unserialize, you can do this using WP CLI shell command, regular PHP command line, or this online php deserializer

Here is what mine looked like:

After that it will tell you where the error lies, but not how to fix it. For me it was the first character which should be a lower case a instead of an uppercase A thus it broke the serialized field. That is something to check that all the field define types like a, i, s, etc are lowercase. If you have a very big field and struggle to find the offset since it’s in bytes, you can use this function to extract the part of the string that it is referring to: php mb_strcut

After I made that change and updated it in the database the menu was saving again.