wp_delete_auto_drafts() deletes links in menus

Since yesterday the wp_scheduled_auto_draft_delete cron job somehow deletes all page entries from our menus. All entries in the table wp_postmeta with meta_key = '_menu_item_object' and meta_value = 'page' get deleted in the process. By tracing back the DELETE query I found out that the menu entries are deleted by a function called _wp_delete_post_menu_item. I got the following stacktrace for that function:

#0  _wp_delete_post_menu_item(3244)
#1  call_user_func_array(_wp_delete_post_menu_item, Array ([0] => 3244))
called at [/srv/www/htdocs/biva/wp-includes/plugin.php:470]
#2  do_action(delete_post, 3244) called at
[/srv/www/htdocs/biva/wp-includes/post.php:2483]
#3  wp_delete_post(3244, 1) called at
[/srv/www/htdocs/biva/wp-includes/post.php:5645]
#4  wp_delete_auto_drafts()
#5  call_user_func_array(wp_delete_auto_drafts, Array ()) called at
[/srv/www/htdocs/biva/wp-includes/plugin.php:546]
#6  do_action_ref_array(wp_scheduled_auto_draft_delete, Array ()) called
at [/srv/www/htdocs/biva/wp-cron.php:100]

But I’m stuck here, I’m not a wordpress developer and I have no clue why deleting old drafts results in deleting attached menu entries although the main pages still exist.

Workaround:
As a workaround we added the following to the top of wp-config, wich will disable wp-cron:

/** Disable wp-cron to prevent menu from getting deleted. Detailed Bug description:
http://wordpress.stackexchange.com/questions/145953 **/
define('DISABLE_WP_CRON', true);

If you are experiencing similar difficulties you might try locking the rows in the table wp_postmeta with meta_key = '_menu_item_object' and meta_value = 'page' and then tracing back the error message when WP trys to delete these rows, that’s how we nailed down our error.

Reproduce the Error:
It is definitely the stealth publish plugin that is causing our error, one can easily reproduce it, by doing the following:

  1. Install a fresh WordPress 3.9.1.
  2. Install the stealth publish plugin http://wordpress.org/plugins/stealth-publish.
  3. Create a few test pages and add them to a new menu.
  4. Create a new page, with some dummy content, but do not save it!. Leave the page without saving.
  5. Change the system time 8 days into the future.
  6. The wp_scheduled_auto_draft_delete cron job will run because it runs daily and it hasn’t run for 7 days as soon as you visit your page. It will then find your never saved dummy page delete it as it is older than 7 days and the menu will vanish.
  7. You might need to refresh the page a few times till the effect becomes visible.

I’ve recorded a screencast on how to reproduce the error (but I took not the time to comment it): http://www.youtube.com/watch?v=R9CTggjIfDY

Solutions Collecting From Web of "wp_delete_auto_drafts() deletes links in menus"

This is what normal query run by wp_get_associated_nav_menu_items() looks like:

SELECT wp_posts.* 
FROM   wp_posts 
       INNER JOIN wp_postmeta 
               ON ( wp_posts.id = wp_postmeta.post_id ) 
WHERE  1 = 1 
       AND wp_posts.post_type = 'nav_menu_item' 
       AND (( wp_posts.post_status <> 'trash' 
              AND wp_posts.post_status <> 'auto-draft' )) 
       AND (( wp_postmeta.meta_key = '_menu_item_object_id' 
              AND Cast(wp_postmeta.meta_value AS CHAR) = '3111' )) 
GROUP  BY wp_posts.id 
ORDER  BY wp_posts.post_date DESC 

This is what yours looks like:

SELECT wp_posts.* 
FROM   wp_posts 
       INNER JOIN wp_postmeta 
               ON ( wp_posts.id = wp_postmeta.post_id ) 
       LEFT JOIN wp_postmeta AS mt1 
              ON ( wp_posts.id = mt1.post_id 
                   AND mt1.meta_key = '_stealth-publish' ) 
       INNER JOIN wp_postmeta AS mt2 
               ON ( wp_posts.id = mt2.post_id ) 
WHERE  1 = 1 
       AND wp_posts.post_type = 'nav_menu_item' 
       AND (( wp_posts.post_status <> 'trash' 
              AND wp_posts.post_status <> 'auto-draft' )) 
       AND ( ( wp_postmeta.meta_key = '_menu_item_object_id' 
               AND Cast(wp_postmeta.meta_value AS CHAR) = '3244' ) 
              OR mt1.post_id IS NULL 
              OR ( mt2.meta_key = '_stealth-publish' 
                   AND Cast(mt2.meta_value AS CHAR) != '1' ) ) 
GROUP  BY wp_posts.id 
ORDER  BY wp_posts.post_date DESC 

It would be my guess that _stealth-publish stuff has no business whatsoever interfering with querying for nav menu items.