Skip to content

Action Scheduler queue cleanup fatals with "Unidentified action" after concurrent deletion #1369

Description

@pirate-bot

Summary

Visualizer's scheduled-action cleanup can terminate with an uncaught InvalidArgumentException when an action selected for failure processing is deleted or otherwise disappears before its status update. Cleanup is expected to tolerate that race and continue processing the queue; instead, the request ends in a fatal error. This can interrupt scheduled work on an affected site.

Customer context

  • Product / area: Visualizer scheduled database refresh / bundled Action Scheduler
  • Version: 4.0.7
  • Environment: WordPress site; PHP, WordPress, database, and hosting versions were not provided
  • Integration / third party: Bundled woocommerce/action-scheduler 3.9.3
  • Reported error / symptom: One captured uncaught InvalidArgumentException states that an action could not be marked failed because another process may have deleted it
  • Impact: One queue-cleanup request terminated; broader scheduling impact is unknown

Reproduction notes

Runtime reproduction was not performed. Evidence-backed inferred workflow:

  1. Run Visualizer 4.0.7 with its bundled Action Scheduler active.
  2. Have queue cleanup query a stale action in in-progress status.
  3. Between that query and the status update, let another process delete the action or alter it so the update affects no row.
  4. Observe an uncaught InvalidArgumentException from ActionScheduler_DBStore::mark_failure().

The submitted crash log confirms steps 2–4 occurred on one site, but it does not identify the competing process or action hook.

Diagnosis

Conclusion

The fatal is confirmed as a dependency race exposed by Visualizer's bundled Action Scheduler. The captured stack shows ActionScheduler_QueueCleaner::mark_failures() selecting an action and ActionScheduler_DBStore::mark_failure() throwing when the subsequent database update affects no row. Inspection confirms that the cleaner does not handle that exception. Upstream issue woocommerce/action-scheduler#970 describes the same fatal, remains open, and is labeled as a high-priority bug.

Where this likely occurs

  • Scheduled database-refresh subsystem: classes/Visualizer/Module/Setup.php — Visualizer_Module_Setup::schedule_refresh_db_action() lines 486–504 schedules the visualizer_schedule_refresh_db recurring action through Action Scheduler when its API is available.
  • Dependency bootstrap: index.php — Visualizer startup callback approx. lines 148–157 loads the bundled Action Scheduler entry point.
  • Dependency selection: composer.lock — package record lines 181–221 locks woocommerce/action-scheduler 3.9.3 at commit c58cdbab17651303d406cd3b22cf9d75c71c986c; tag v4.0.7 contains this lock.
  • Queue-cleanup path: vendor/woocommerce/action-scheduler/classes/ActionScheduler_QueueCleaner.php — ActionScheduler_QueueCleaner::mark_failures() lines 206–228 queries stale running action IDs and calls mark_failure() for each without an exception boundary.
  • Data-store path: vendor/woocommerce/action-scheduler/classes/data-stores/ActionScheduler_DBStore.php — ActionScheduler_DBStore::mark_failure() lines 1215–1233 throws the reported exception when its update affects no row.
  • Git history: Visualizer commit bc14d4b8 introduced the Action Scheduler dependency and loading path; it is present from v4.0.0 through v4.0.7. This identifies the feature boundary but does not prove a regression because the earlier releases did not provide the same scheduler workflow.

Engineering notes

Visualizer loads its packaged Action Scheduler conditionally and uses the global as_* API. On sites with multiple Action Scheduler copies, normal package version arbitration may determine which copy executes; the crash path identifies a copy under Visualizer's packaged vendor/woocommerce/action-scheduler directory. The race window lies between querying stale running actions and updating each action, so concurrent cleanup, deletion, or another process changing the row can trigger it. The exact action hook and the process that removed or changed the reported row are unknown.

Test coverage status

No relevant Visualizer unit or e2e coverage was found during inspection for Action Scheduler cleanup, concurrent action deletion, or mark_failure() exceptions. The upstream dependency's test suite was not available in the Visualizer checkout.

What to verify or explore next

  • May be worth reproducing with Visualizer 4.0.7 by placing an action in stale in-progress state, deleting or changing it after the cleaner query, and running queue cleanup.
  • If reproducible, checking both WP-Cron and asynchronous queue-runner cleanup contexts may establish the affected execution surfaces.
  • May be worth confirming behavior when another active plugin registers a newer or older Action Scheduler copy before Visualizer.
  • Checking the packaged release ZIP can confirm that its dependency source exactly matches lock reference c58cdbab17651303d406cd3b22cf9d75c71c986c.

Unknowns / follow-up

The report contains one occurrence and no site load, database, concurrency, or action-hook details. It is unknown whether the vanished action belonged to Visualizer or another component sharing the scheduler tables.

Confidence

Confidence: 94/100

The captured stack exactly matches a documented race in the Action Scheduler dependency bundled by Visualizer 4.0.7, and repository inspection confirms that the exception from mark_failure() is not handled by the queue cleaner. Upstream issue woocommerce/action-scheduler#970 independently classifies the same fatal as a bug; no matching Visualizer issue was found.


Source: automated uninstall feedback — visualizer, 2026-08-30
Generated by bug-report-triage (ID: bug-report-triage_6a950a8cd8e195.34071552)

Activity

  1. added theissue type on Aug 31, 2026
  2. self-assigned this
    on Sep 17, 2026
  3. milicavs commented on Oct 5, 2026

    @milicavs

    🤖 Automated QA Repro Steps

    Visualizer bundles the Action Scheduler background-task library. When the queue cleanup marks a stale scheduled task as failed, but that task has meanwhile been deleted — or an overlapping cleanup run already marked it — the whole request ends in a fatal error instead of skipping that task and carrying on, interrupting scheduled work on the site. This cannot be triggered through any normal click path: it needs two processes touching the same scheduled task at once, so step 1 forces that condition instead.

    Reproduce on Visualizer 4.0.9:

    1. Install Code Snippets, add a snippet titled V1369 vanished action set to run everywhere, with this code, and activate it:
      add_action( 'admin_notices', function () {
          if ( ! isset( $_GET['v1369'] ) || ! class_exists( 'ActionScheduler' ) ) {
              return;
          }
          ActionScheduler::store()->mark_failure( 999999999 );
          echo '<div class="notice notice-success"><p>Survived the vanished action.</p></div>';
      } );
    2. Open /wp-admin/?v1369=1.
    • Bug: the admin page dies — "There has been a critical error on this website", with Uncaught InvalidArgumentException: Unidentified action 999999999 in debug.log or on screen.
    • Fixed: the admin page loads normally and shows the green "Survived the vanished action." notice.
    Edge cases
    • A scheduled task that genuinely fails — still listed as Failed on the Scheduled Actions screen, never silently marked complete.
    • Visualizer → Charts with existing charts, and a chart set to refresh on a schedule — charts render and the scheduled refresh still runs on time.
    • WooCommerce active (it ships its own copy of the same library) — WooCommerce → Status → Scheduled Actions still lists actions and they complete.
    • Deactivate and reactivate Visualizer — no fatal on activation, Scheduled Actions screen still loads.
    • Deactivate the snippet afterwards — the admin behaves exactly as before on both builds.

    Fix: #1381 (open)

  4. pirate-bot commented on Oct 5, 2026

    @pirate-bot
    ContributorAuthor

    QA agent — 🟢 Passing checks · Queue cleanup survives deleted and already-failed actions without stopping scheduled work.

    The base build stopped both locked-row cleanup runs with “Unidentified action.” The candidate finished both and processed the next task, including overlapping cron runs and the async handler. Normal failures stayed Failed, while real database errors still surfaced. All seven checks and the seven added regression tests passed; WooCommerce’s scheduler copy also worked.

    Worth a human look

    1. Try Action Scheduler queue cleanup fatals with "Unidentified action" after concurrent deletion #1369’s automated QA hint for chart rendering and timed data refresh with the customer’s charts and data source (not provided; the Pro refresh journey is unchanged by this PR).
    2. Review the generic Scheduled Actions “Successfully executed” notice shown for throwing or deleted actions; the stored status and log correctly showed failure or absence.
    Checks (7 passed)
    • ✅ Visualizer registers its replacement for the default Action Scheduler database store, while leaving non-default stores unchanged, and Scheduled Actions loads normally.
    • ✅ When a locked stale in-progress action is deleted by another database session, queue cleanup waits for the lock and finishes normally, then continues processing the remaining queue.
    • ✅ When another database session marks a locked stale action failed first, overlapping queue cleanup finishes normally and continues processing the remaining queue.
    • ✅ Normal queue cleanup marks a stale in-progress action failed, and that status remains visible after reloading Scheduled Actions.
    • ✅ Running a genuinely throwing scheduled action from Scheduled Actions records it as Failed rather than Complete, with its error visible after reload.
    • ✅ The scheduled-action runner survives an action that is deleted during execution and then throws, without an Unidentified action error, and the deleted action remains absent.
    • ✅ A real database error while marking an action failed still throws an InvalidArgumentException rather than being silently treated as a harmless race.

    Full report, evidence and session trail

  5. kushh23 commented on Oct 5, 2026

    @kushh23
    Contributor

    🧪 Test in Playground

    Link Build Bot check
    Reproduce the bug Visualizer 4.0.9 (released) ✅ Reproduced: the page stops with "QA #1369: queue cleanup died" and Uncaught InvalidArgumentException: Unidentified action 8, and the Scheduled Actions screen never loads
    Verify the fix PR #1381 build fb00b81 (Plugins shows 4.0.8-dev.b7699ad) ✅ Fixed: Scheduled Actions loads with the green notice "queue cleanup finished normally", and QA Logs shows the action was deleted mid-cleanup and skipped

    Setup: a small must-use plugin recreates the race on every visit to Tools → Scheduled Actions with &v1369=1: it adds a stale in-progress Visualizer refresh action, deletes it right before the queue cleaner marks it failed (as a second process would), then runs the real cleaner. The links land there. Tools → QA Logs shows debug.log.
    Try: reload the landing page to run the race again, then compare the two sides; in Tools → QA Logs, the release ends at cleanup_died and the PR build at cleanup_finished.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions