Skip to content

Fix plugin reinstall failure after deletion in Sandbox runtime#4044

Merged
wojtekn merged 7 commits into
trunkfrom
ai/agent/stu-1931-1782979528
Jul 10, 2026
Merged

Fix plugin reinstall failure after deletion in Sandbox runtime#4044
wojtekn merged 7 commits into
trunkfrom
ai/agent/stu-1931-1782979528

Conversation

@agent-sandbox-automattic

Copy link
Copy Markdown
Contributor

Related issues

Closes https://linear.app/a8c/issue/STU-1931

How AI was used in this PR

AI was used to investigate the root cause, design the fix, and write the code.

Proposed Changes

Fixes a bug where manually reinstalling a plugin (via zip upload) after deleting it through the WordPress Plugins admin screen would fail with:

The destination directory already exists and could not be removed.

The workaround was to restart the Studio site before reinstalling.

Root cause: In the Sandbox (PHP WASM) runtime, PHP runs as a persistent process whose internal stat/realpath caches persist across HTTP requests (unlike native PHP, where each request forks a fresh PHP process). After deleting a plugin in one request, PHP's cache can retain a stale "directory exists" result for the plugin path. On the next request (reinstalling the same plugin), WordPress calls is_dir() on the destination — the stale cache says it exists — then tries to delete it and gets an ENOENT-equivalent failure from the VFS layer, producing the error.

Fix: A new mu-plugin (0-clear-stat-cache-before-upgrade.php) hooks into WordPress's upgrader_pre_install filter and calls clearstatcache(true). This flushes both PHP's stat cache and its realpath cache immediately before any upgrade or install operation, ensuring is_dir() gets a fresh result from the real filesystem.

This is a no-op for the native PHP runtime (each request already starts with a clean process and empty caches).

Testing Instructions

  • Create a Studio site using the Sandbox PHP runtime
  • Navigate to wp-admin → Plugins → Add New Plugin → Upload Plugin, and install any plugin via zip (or install from the DotOrg directory)
  • Delete the plugin via the Plugins list
  • Upload/install the same plugin again
  • Before fix: the second install fails with "The destination directory already exists and could not be removed"
  • After fix: the second install succeeds

Pre-merge Checklist

  • Tested the fix against the reproduction steps described in STU-1931 (Sandbox runtime only; native PHP unaffected)
  • No UI/CSS changes — no visual review required
  • CI checks pass

Linear: STU-1931

Co-authored-by: wojtekn wojtek.naruniec@automattic.com

This PR was auto-generated by Agent Sandbox. Please review carefully before merging.

matticbot and others added 5 commits July 2, 2026 08:21
In the Sandbox (PHP WASM) runtime, PHP runs as a persistent process whose
internal stat/realpath caches persist across HTTP requests. After a plugin is
deleted via wp-admin, WordPress calls rmdir() which clears the stat-cache for
that exact path. However on the next request (reinstalling the same plugin) the
path may resolve differently (e.g. with/without trailing slash, via realpath)
and the stale cache entry causes is_dir() to return true for a directory that
no longer exists. WordPress then tries to delete it before installing, gets an
ENOENT-equivalent failure from the VFS, and returns the error "The destination
directory already exists and could not be removed."

Add a mu-plugin that hooks into upgrader_pre_install and calls
clearstatcache(true) to flush both the stat cache and the realpath cache before
any upgrade/install operation. This ensures WordPress gets an accurate
filesystem view and can proceed with the installation.

Co-authored-by: wojtekn <wojtek.naruniec@automattic.com>
@wojtekn wojtekn self-assigned this Jul 8, 2026
@wojtekn wojtekn marked this pull request as ready for review July 8, 2026 10:20
@wpmobilebot

wpmobilebot commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 270b16e vs trunk

app-size

Metric trunk 270b16e Diff Change
App Size (Mac) 1419.29 MB 1412.19 MB 7.10 MB 🟢 -0.5%

site-editor

Metric trunk 270b16e Diff Change
load 1025 ms 1065 ms +40 ms ⚪ 0.0%

site-startup

Metric trunk 270b16e Diff Change
siteCreation 6526 ms 6484 ms 42 ms ⚪ 0.0%
siteStartup 2385 ms 2399 ms +14 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@wojtekn wojtekn merged commit 9da2e4d into trunk Jul 10, 2026
11 checks passed
@wojtekn wojtekn deleted the ai/agent/stu-1931-1782979528 branch July 10, 2026 09:43
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.

3 participants