Hey @ryancramerdesign
and here's an second error, that Claude reported during his investigation, wich I mentioned in my previous issue report.
Lets me please know if it's OK for you, sending issues this way.
Cheers Jens.
Short description of the issue
Pageimages::getAllVariations() constructs a \DirectoryIterator over the page's files
directory without checking that the directory exists. If it does not — which is the normal
state for a saved page that has never had a file uploaded, and a transient state for a page
being cloned — the request dies with an unhandled
UnexpectedValueException: DirectoryIterator::__construct(...): Failed to open directory.
wire/core/Pageimages/Pageimages.php:143 (3.0.271 dev; before the core file reorganization:
wire/core/Pageimages.php:143):
foreach(new \DirectoryIterator($this->path()) as $file) {
Pagefiles::path() → PagefilesManager::___path() only computes the path, it never creates
the directory, so this is reachable through the documented public API.
The same unguarded pattern exists a second time, in
wire/core/Pageimages/PageimageVariations.php:335 inside find():
$dir = new \DirectoryIterator($this->pagefiles->path);
is_dir does not appear anywhere in either file.
Expected behavior
A missing files directory means "no variations". getAllVariations() returns an empty array
(respectively an empty result / count of 0 from PageimageVariations::find()); the request
continues.
Actual behavior
Unhandled UnexpectedValueException, request aborted.
Steps to reproduce the issue
- Create a page with an images field and save it. Do not upload anything, so
site/assets/files/<id>/ is never created.
- Call
$page->images->getAllVariations(); (API, template, or Tracy console).
- →
UnexpectedValueException: DirectoryIterator::__construct(/path/to/site/assets/files/<id>/): Failed to open directory.
For the second location, the same happens whenever Pageimage::getVariations() runs while the
files directory of the page holding the image is absent — e.g. during a clone, before the files
have been copied across.
Optional: Screenshots/Links that demonstrate the issue
From a production site, 18 logged crashes between 09/2025 and 08/2026 in two shapes.
1. Duplicating an item inside a (Matrix) Repeater — 11 occurrences:
/cms/page/edit/?id=<id>&field=<repeater>&repeater_clone=<id>&repeater_clone_to=<id>
→ UnexpectedValueException: DirectoryIterator::__construct(
/path/to/site/assets/files/<id>/): Failed to open directory
(wire/core/Pageimages.php:143)
This one is particularly unpleasant in practice: the clone itself completes in the database,
only the rendering of the response is aborted. The editor sees nothing happen and clicks again
— in the logs, four times in a row on one day and four times again a week later, each attempt
leaving another orphaned repeater page behind.
2. Saving a newly created page — 7 occurrences, via
PageimageDebugInfo::getBasicDebugInfo() → Pageimage::getVariations() →
PageimageVariations::find() → DirectoryIterator (i.e. the second code location above).
This variant only occurs in a debug context ($config->debug / TracyDebugger). In one case it
aborted a save mid-flight, leaving the page with incomplete multi-language status flags — which
then looked to everyone like an editor had forgotten a language tab.
Optional: Suggestion for a possible fix
An is_dir() guard in both places, e.g.:
$path = $this->path();
if(is_dir($path)) {
foreach(new \DirectoryIterator($path) as $file) {
// ...
}
}
Setup/Environment
- ProcessWire version: observed on 3.0.255; both code locations are unchanged on the current
dev branch (3.0.271, checked 2026-08-31)
- (Optional) PHP version: 8.x
- (Optional) Any 3rd party modules that are installed and could be related to the issue:
TracyDebugger (triggers the PageimageDebugInfo variant; the getAllVariations() variant
needs no modules), FieldtypeRepeaterMatrix
Hey @ryancramerdesign
and here's an second error, that Claude reported during his investigation, wich I mentioned in my previous issue report.
Lets me please know if it's OK for you, sending issues this way.
Cheers Jens.
Short description of the issue
Pageimages::getAllVariations()constructs a\DirectoryIteratorover the page's filesdirectory without checking that the directory exists. If it does not — which is the normal
state for a saved page that has never had a file uploaded, and a transient state for a page
being cloned — the request dies with an unhandled
UnexpectedValueException: DirectoryIterator::__construct(...): Failed to open directory.wire/core/Pageimages/Pageimages.php:143(3.0.271 dev; before the core file reorganization:wire/core/Pageimages.php:143):Pagefiles::path()→PagefilesManager::___path()only computes the path, it never createsthe directory, so this is reachable through the documented public API.
The same unguarded pattern exists a second time, in
wire/core/Pageimages/PageimageVariations.php:335insidefind():is_dirdoes not appear anywhere in either file.Expected behavior
A missing files directory means "no variations".
getAllVariations()returns an empty array(respectively an empty result / count of 0 from
PageimageVariations::find()); the requestcontinues.
Actual behavior
Unhandled
UnexpectedValueException, request aborted.Steps to reproduce the issue
site/assets/files/<id>/is never created.$page->images->getAllVariations();(API, template, or Tracy console).UnexpectedValueException: DirectoryIterator::__construct(/path/to/site/assets/files/<id>/): Failed to open directory.For the second location, the same happens whenever
Pageimage::getVariations()runs while thefiles directory of the page holding the image is absent — e.g. during a clone, before the files
have been copied across.
Optional: Screenshots/Links that demonstrate the issue
From a production site, 18 logged crashes between 09/2025 and 08/2026 in two shapes.
1. Duplicating an item inside a (Matrix) Repeater — 11 occurrences:
This one is particularly unpleasant in practice: the clone itself completes in the database,
only the rendering of the response is aborted. The editor sees nothing happen and clicks again
— in the logs, four times in a row on one day and four times again a week later, each attempt
leaving another orphaned repeater page behind.
2. Saving a newly created page — 7 occurrences, via
PageimageDebugInfo::getBasicDebugInfo()→Pageimage::getVariations()→PageimageVariations::find()→DirectoryIterator(i.e. the second code location above).This variant only occurs in a debug context (
$config->debug/ TracyDebugger). In one case itaborted a save mid-flight, leaving the page with incomplete multi-language status flags — which
then looked to everyone like an editor had forgotten a language tab.
Optional: Suggestion for a possible fix
An
is_dir()guard in both places, e.g.:Setup/Environment
dev branch (3.0.271, checked 2026-08-31)
TracyDebugger (triggers the
PageimageDebugInfovariant; thegetAllVariations()variantneeds no modules), FieldtypeRepeaterMatrix