{"id":19005235,"date":"2026-08-28T06:22:00","date_gmt":"2026-08-28T06:22:00","guid":{"rendered":"https:\/\/wordpress.org\/support\/topic\/cartflows_update_knowledge_base_data-10k-tasks-failed\/"},"modified":"2026-08-28T06:22:00","modified_gmt":"2026-08-28T06:22:00","slug":"cartflows_update_knowledge_base_data-10k-tasks-failed","status":"publish","type":"topic","link":"https:\/\/wordpress.org\/support\/topic\/cartflows_update_knowledge_base_data-10k-tasks-failed\/","title":{"rendered":"cartflows_update_knowledge_base_data =&gt; 10K tasks failed"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Hi all,<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;m reporting a problem that has generated <strong>9,162 failed scheduled actions<\/strong> on my production store. The failure is not intermittent as the action has never once succeeded.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Environment<\/strong><\/p>\n\n\n\n<ul>\n<li>CartFlows 3.2.0 (free)<\/li>\n\n\n\n<li>WordPress 7.1 \/ PHP 8.4 \/ WooCommerce<\/li>\n\n\n\n<li><code>define( 'DISABLE_WP_CRON', true );<\/code> in <code>wp-config.php<\/code>, with an external cron service calling <code>\/wp-cron.php<\/code> every 10 minutes<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Symptom<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">WooCommerce \u2192 Status \u2192 Scheduled Actions:<br \/>All (11,341) | Canceled (13) | Complete (2,146) | Failed (9,162) | Pending (20)<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/wordpress.org\/2dac46ac-f904-4b0d-bc26-658b2903f9f0\" alt=\"\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Almost every failed row is the same hook. The log for each one is identical:<br \/>1. action created <br \/>2. action started via WP Cron <br \/>3. action failed via WP Cron: Scheduled action for cartflows_update_knowledge_base_data will not be executed as no callbacks are registered. <br \/>4. This action appears to be consistently failing. A new instance will not be scheduled.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Action Scheduler itself seems healthy : 2,146 actions from other plugins complete normally.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Possible root cause<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The callback is registered inside an admin-only code path, but Action Scheduler executes it in a non-admin context.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In <code>classes\/class-cartflows-loader.php<\/code>, the file holding the callback is included only in admin:<br \/>if ( is_admin() ) { include_once CARTFLOWS_DIR . &#8216;classes\/class-cartflows-admin.php&#8217;; }<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And in <code>classes\/class-cartflows-admin.php<\/code>, <code>init_hooks()<\/code> bails out again before registering anything:<br \/>public function init_hooks() { if ( ! is_admin() ) { return; } \/\/ &#8230; add_action( &#8216;init&#8217;, array( $this, &#8216;run_scheduled_docs_job&#8217; ) ); add_action( &#8216;cartflows_update_knowledge_base_data&#8217;, array( $this, &#8216;cartflows_update_knowledge_base_data&#8217; ) );<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">During a <code>\/wp-cron.php<\/code> request <code>is_admin()<\/code> returns <code>false<\/code>, so the class is never loaded and <code>add_action( 'cartflows_update_knowledge_base_data', ... )<\/code> never runs. Action Scheduler finds no callback and fails the action as the log states.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why it loops forever<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Action Scheduler correctly applies its own safety brake (&#8220;A new instance will not be scheduled&#8221;), but <code>run_scheduled_docs_job()<\/code> defeats it:<br \/>public function run_scheduled_docs_job() { if ( false === as_next_scheduled_action( &#8216;cartflows_update_knowledge_base_data&#8217; ) &amp;&amp; ! wp_installing() ) { as_schedule_recurring_action( time(), WEEK_IN_SECONDS, &#8216;cartflows_update_knowledge_base_data&#8217; ); } }<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is hooked to <code>init<\/code> in the admin. Because the previous action ended as <code>failed<\/code> rather than <code>pending<\/code>, <code>as_next_scheduled_action()<\/code> returns <code>false<\/code>, so a brand-new recurring action is created <strong>on every admin page load<\/strong>. The cycle seems to be:<\/p>\n\n\n\n<ol start=\"1\">\n<li>Admin page load \u2192 no pending action \u2192 schedule a new one<\/li>\n\n\n\n<li>WP-Cron runs it \u2192 no callback registered \u2192 fail<\/li>\n\n\n\n<li>Action Scheduler refuses to reschedule<\/li>\n\n\n\n<li>Next admin page load \u2192 back to step 1<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The row is supposed to run weekly. In practice new rows appear every few minutes while I browse wp-admin. I also see duplicate rows sharing the same claim ID (e.g. <code>809269<\/code>, <code>809287<\/code> each appearing twice), which suggests concurrent admin requests racing in <code>run_scheduled_docs_job()<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why this may not reproduce on your test sites<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You may not be able to reproduce it on your side because of the cron-job setup.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Impact<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beyond the noise, the <code>wp_actionscheduler_actions<\/code> and <code>..._logs<\/code> tables grow without bound (over 11,000 rows for me). My database was under memory pressure, so this is a real cost for me.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Suggested fix<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do you think that registering the Action Scheduler callback outside the <code>is_admin()<\/code> guard, since Action Scheduler is not an admin-only subsystem could work ? It would also be nice to have <code>run_scheduled_docs_job()<\/code> respect Action Scheduler&#8217;s &#8220;consistently failing&#8221; state instead of unconditionally recreating the action whenever none is pending.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Thanks for you help !<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"template":"","class_list":["post-19005235","topic","type-topic","status-publish","hentry"],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/support\/wp-json\/wp\/v2\/topic\/19005235","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/support\/wp-json\/wp\/v2\/topic"}],"about":[{"href":"https:\/\/wordpress.org\/support\/wp-json\/wp\/v2\/types\/topic"}],"version-history":[{"count":0,"href":"https:\/\/wordpress.org\/support\/wp-json\/wp\/v2\/topic\/19005235\/revisions"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/support\/wp-json\/wp\/v2\/media?parent=19005235"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}