Skip to content

fix: timeout stalled plugin updates - #776

Open
Pranjal1423 wants to merge 6 commits into
WordPress:trunkfrom
Pranjal1423:fix/plugins-update-timeout-765
Open

fix: timeout stalled plugin updates#776
Pranjal1423 wants to merge 6 commits into
WordPress:trunkfrom
Pranjal1423:fix/plugins-update-timeout-765

Conversation

@Pranjal1423

@Pranjal1423 Pranjal1423 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #765

When a plugin update request stalls indefinitely (e.g. server-side hang, network freeze, or unresponsive proxy), the promise never resolves or rejects. Because the plugins app uses a serial update queue (updateQueue), a hanging promise causes the queue to remain locked forever, leaving the plugin row permanently in a busy state and preventing any subsequent updates (both single and bulk) from running.

This PR adds an execution timeout (DEFAULT_TIMEOUT_MS = 60_000) to queued update operations via Promise.race(). If an update promise fails to resolve within the timeout period, it is rejected with an Update timeout error. This allows the existing try/catch/finally error handling in runUpdate() and runBulkUpdate() to catch the error, show a failure notice, release the busy state on the row, and allow subsequent queued updates to proceed.


Changes

  • apps/plugins/parts/update-queue.ts: Wrapped the task invocation in drain() with Promise.race() against a configurable timeout timer (defaulting to 60s), ensuring the timeout timer is always cleaned up on completion.
  • tests/vitest/plugins-update-queue.test.ts: Added comprehensive unit test coverage verifying:
    • Updates completing before the timeout resolve normally.
    • Stalled updates reject with a timeout error after the configured timeout.
    • Subsequent updates in the queue continue executing after a preceding task times out.
    • Direct task rejections before timeout propagate cleanly without delay.

How to test

  1. Open the Plugins app in OpenStation.
  2. Trigger a plugin update under conditions where the request stalls or times out.
  3. Verify that after the timeout period:
    • A failure notice/toast is displayed indicating the update failed.
    • The busy indicator on the plugin row clears and the row is unlocked.
    • Subsequent plugin updates can be clicked and executed without needing to reload the app.
  4. Run automated tests: npm run test:js tests/vitest/plugins-update-queue.test.ts

Before the Fix

$ npm run test:js tests/vitest/plugins-update-queue.test.ts

> openstation@1.1.6 test:js
> vitest run tests/vitest/plugins-update-queue.test.ts

 RUN  v4.1.5 /Users/pranjalwadhwani/Desktop/desktop-mode
 tests/vitest/plugins-update-queue.test.ts (7 tests | 7 failed) 6ms
     × has a default timeout of 60 seconds 3ms
     × resolves normally when the job completes before timeout 0ms
     × rejects with custom error when the job fails normally 0ms
     × rejects with a timeout error if a job stalls and does not settle 0ms
     × unblocks subsequent queued jobs after a timed-out job 0ms
     × executes jobs in strict FIFO order 0ms
     × recovers from a stalled update: shows failure toast, clears busy state, and re-enables button 0ms

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 7 ⎯⎯⎯⎯⎯⎯⎯

 Test Files  1 failed (1)
      Tests  7 failed (7)
   Duration  1.15s

After the Fix


$ npm run test:js tests/vitest/plugins-update-queue.test.ts

> openstation@1.1.6 test:js
> vitest run tests/vitest/plugins-update-queue.test.ts

 RUN  v4.1.5 /Users/pranjalwadhwani/Desktop/desktop-mode

 ✓ tests/vitest/plugins-update-queue.test.ts (7 tests) 13ms
   ✓ Plugins update queue (enqueueUpdateJob)
     ✓ has a default timeout of 60 seconds
     ✓ resolves normally when the job completes before timeout
     ✓ rejects with custom error when the job fails normally
     ✓ rejects with a timeout error if a job stalls and does not settle
     ✓ unblocks subsequent queued jobs after a timed-out job
     ✓ executes jobs in strict FIFO order
   ✓ Plugins update action with timeout integration
     ✓ recovers from a stalled update: shows failure toast, clears busy state, and re-enables button

 Test Files  1 passed (1)
      Tests  7 passed (7)
   Duration  1.17s
Open WordPress Playground Preview

@AllTerrainDeveloper AllTerrainDeveloper left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@AllTerrainDeveloper
AllTerrainDeveloper enabled auto-merge (squash) September 11, 2026 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A stalled plugin update never times out, and wedges the entire update queue until reload — not just its own row

2 participants