Summary
After upgrading a module (e.g. via the ProcessWire Upgrade module), the admin reports the upgrade succeeded but the module's info (version, etc.) stays at the old value. It only corrects itself on a later, separate refresh — so the upgrade appears to "not take" on the first try.
Cause
Commit 5bb7f4f1 ("Add mtime snapshot fast-path to Modules::refresh()") added an optimization: ___refresh() builds a [mtime, size] snapshot of every module file and returns early — skipping the module-info rebuild — when the snapshot matches the previously cached one.
The problem is the ordering of events within a single module-upgrade request:
- The upgrade replaces the module's files on disk, then calls
$modules->refresh() in that same request (ModulesDownloader::unzipModule()).
- For an update of an already-loaded module, the old class is already in memory, so the info rebuilt during this request can still reflect the old code.
- That same
refresh() then saves a file snapshot that already matches the new files on disk.
- On the next request,
refresh() sees the snapshot matches → takes the early return → never rebuilds → the stale info persists.
This is why it takes a second attempt/refresh for the upgrade to appear.
Reproduction
- Install a module.
- In one request: load the module, replace its files with a newer version, then call
$modules->refresh() (this is what the upgrade flow does).
- In subsequent requests,
$modules->getModuleInfo() keeps reporting the old version; even a fresh-process refresh() returns early and never rebuilds.
Verified against a real install with a throwaway module: the reported version stayed stale across every subsequent request until the snapshot cache was manually cleared.
Expected
A module upgrade should be reflected on the next refresh, not require a second manual refresh.
Affected
wire/core/Modules/Modules.php — ___refresh()
- Introduced in
5bb7f4f1 (2026-06-25).
A fix + regression test is proposed in processwire/processwire PR (linked below).
Summary
After upgrading a module (e.g. via the ProcessWire Upgrade module), the admin reports the upgrade succeeded but the module's info (version, etc.) stays at the old value. It only corrects itself on a later, separate refresh — so the upgrade appears to "not take" on the first try.
Cause
Commit
5bb7f4f1("Add mtime snapshot fast-path toModules::refresh()") added an optimization:___refresh()builds a[mtime, size]snapshot of every module file and returns early — skipping the module-info rebuild — when the snapshot matches the previously cached one.The problem is the ordering of events within a single module-upgrade request:
$modules->refresh()in that same request (ModulesDownloader::unzipModule()).refresh()then saves a file snapshot that already matches the new files on disk.refresh()sees the snapshot matches → takes the early return → never rebuilds → the stale info persists.This is why it takes a second attempt/refresh for the upgrade to appear.
Reproduction
$modules->refresh()(this is what the upgrade flow does).$modules->getModuleInfo()keeps reporting the old version; even a fresh-processrefresh()returns early and never rebuilds.Verified against a real install with a throwaway module: the reported version stayed stale across every subsequent request until the snapshot cache was manually cleared.
Expected
A module upgrade should be reflected on the next refresh, not require a second manual refresh.
Affected
wire/core/Modules/Modules.php—___refresh()5bb7f4f1(2026-06-25).A fix + regression test is proposed in processwire/processwire PR (linked below).