Module version: 2.2.2 (2026-08-23) · ProcessWire: 3.0.271 · PHP: 8.5
Summary
FieldtypeSelectExtOption::getExtOptions() ends with:
$this->errors('clear all'); // line 565
In ProcessWire, all does not mean "all errors belonging to this object" — it means "all errors in the global $notices, regardless of which object added them". Wire::messages(), which errors() delegates to, has two branches (wire/core/Wire/Wire.php, PW 3.0.271):
if(in_array('all', $options)) {
$notices = $this->wire()->notices;
$value = $this->wire(new Notices());
foreach($notices as $notice) {
if($notice->getName() != $type) continue;
$value->add($notice);
if($clear) $notices->remove($notice); // clear global
}
if($clear) $this->_notices[$type] = null; // clear local
} else {
$value = $this->_notices[$type] === null ? $this->wire(new Notices()) : $this->_notices[$type];
// ...
if($clear && $value) {
$this->wire()->notices->removeItems($value); // clear from global notices
}
}
So every time a SelectExtOption field builds its option list, every error notice raised anywhere in that request is silently deleted. Messages and warnings are unaffected.
Impact
Any admin form containing a SelectExtOption field silently swallows all error notices, including ones raised by unrelated hooks and modules.
In our case a Pages::saveReady hook called $this->wire()->error(...) to tell the editor that a checkbox had been auto-unchecked on save. The notice was added correctly, carried across ProcessPageEdit's post-save redirect correctly by Session::___redirect(), and restored correctly by Session::wakeupNotices() — then removed during the form render, before AdminThemeFramework::renderNotices() ran. The editor saw "Saved Page" and the warnings, but never the error.
Logging the contents of $notices at ready and again at shutdown, on the request following the redirect:
1-READY notices{ NoticeMessage:Saved Page: /... ~ NoticeError:Self was unchecked because... ~ NoticeWarning:These pronouns... }
3-SHUTDOWN notices{ NoticeMessage:Saved Page: /... ~ NoticeWarning:These pronouns... ~ NoticeWarning:Website URL - Error 403... }
Only the NoticeError is gone.
Why it is easy to miss
The two early returns above it make the behaviour data-dependent:
if (!$query->rowCount()) return null; // never reaches the clear
// ...
if ($query->rowCount() > self::OPTIONSLIMIT) {
$this->error(...);
return false; // also never reaches the clear
}
$this->errors('clear all');
Our dev server had an empty source table, so it never reproduced; production had rows, so it reproduced every time. That made it look like an environment difference rather than a module bug.
Note also that the $this->error(...) branch returns immediately, so line 565 is only ever reached when this call did not raise an error. It can only be clearing a stale error from an earlier invocation in the same request.
Suggested fix
Dropping all takes the second branch, which returns only this object's own errors and calls removeItems() on exactly those — preserving the intent without touching notices raised elsewhere.
Verified on our production install: the missing error notice reappears, and the module's own "Maximum number of selectable options exceeded" error still behaves as before.
Module version: 2.2.2 (2026-08-23) · ProcessWire: 3.0.271 · PHP: 8.5
Summary
FieldtypeSelectExtOption::getExtOptions()ends with:In ProcessWire,
alldoes not mean "all errors belonging to this object" — it means "all errors in the global$notices, regardless of which object added them".Wire::messages(), whicherrors()delegates to, has two branches (wire/core/Wire/Wire.php, PW 3.0.271):So every time a SelectExtOption field builds its option list, every error notice raised anywhere in that request is silently deleted. Messages and warnings are unaffected.
Impact
Any admin form containing a SelectExtOption field silently swallows all error notices, including ones raised by unrelated hooks and modules.
In our case a
Pages::saveReadyhook called$this->wire()->error(...)to tell the editor that a checkbox had been auto-unchecked on save. The notice was added correctly, carried acrossProcessPageEdit's post-save redirect correctly bySession::___redirect(), and restored correctly bySession::wakeupNotices()— then removed during the form render, beforeAdminThemeFramework::renderNotices()ran. The editor saw "Saved Page" and the warnings, but never the error.Logging the contents of
$noticesatreadyand again at shutdown, on the request following the redirect:Only the
NoticeErroris gone.Why it is easy to miss
The two early returns above it make the behaviour data-dependent:
Our dev server had an empty source table, so it never reproduced; production had rows, so it reproduced every time. That made it look like an environment difference rather than a module bug.
Note also that the
$this->error(...)branch returns immediately, so line 565 is only ever reached when this call did not raise an error. It can only be clearing a stale error from an earlier invocation in the same request.Suggested fix
Dropping
alltakes the second branch, which returns only this object's own errors and callsremoveItems()on exactly those — preserving the intent without touching notices raised elsewhere.Verified on our production install: the missing error notice reappears, and the module's own "Maximum number of selectable options exceeded" error still behaves as before.