Nigel M Rodgers

· 1 min read

update_post_meta does not fire save_post

I built a feature that links the same hymn across different hymn books. It keeps a lookup table in an option and rebuilds it on the save_post hook, so the table is always current.

Then I corrected some lyrics with a script, and the table stopped being current.

The catch

The lyrics live in post meta. My script called update_post_meta(), which writes the meta and fires its own meta hooks. It doesn’t fire save_post, because the post itself wasn’t saved.

Edit a post on the screen and everything works, since the editor saves the post. Change the same value from a script, an import or WP-CLI, and anything hanging off save_post never hears about it.

What to do about it

If your code derives something from post meta, you have two options.

Hook the meta write as well:

add_action( 'updated_post_meta', function ( $meta_id, $post_id, $key ) {
    if ( 'hymn' === $key ) {
        my_rebuild_map();
    }
}, 10, 3 );

That fires once per changed row, so on a bulk import you’d rebuild thousands of times. For a bulk job it’s cheaper to do what I did and call the rebuild once, at the end of the script.

Either way, the rule is worth remembering. save_post means a post was saved, and it doesn’t mean the data changed.