octobercms/october · error · SystemException
Unable to remove settings item before items are loaded.
Error message
Unable to remove settings item before items are loaded.
What it means
SettingsManager::removeSettingItem($owner, $code) removes a registered settings item from the internal $items/$groupedItems maps. Those maps are populated lazily by the protected loadItems() on first real use, and removeSettingItem refuses to run against a not-yet-loaded state: if $this->items is falsy it throws a SystemException. So this is a lifecycle misuse — the removal was requested before anything caused settings items to be loaded (i.e. before plugins' registerSettings() output was collected).
Source
Thrown at modules/system/classes/SettingsManager.php:315
}
/**
* defineSettingsMenuItem
*/
protected function defineSettingsMenuItem(array $config): SettingsMenuItem
{
return (new SettingsMenuItem)->useConfig($config);
}
/**
* removeSettingItem using its owner and code
* @param string $owner
* @param string $code
*/
public function removeSettingItem($owner, $code)
{
if (!$this->items) {
throw new SystemException('Unable to remove settings item before items are loaded.');
}
$itemKey = $this->makeItemKey($owner, $code);
unset($this->items[$itemKey]);
if ($this->groupedItems) {
foreach ($this->groupedItems as $category => $items) {
if (isset($items[$itemKey])) {
unset($this->groupedItems[$category][$itemKey]);
}
}
}
}
/**
* setContext sets the navigation context. The owner specifies the setting items owner
* plugin or module in the format Vendor.Module. The code specifies the settings item code.
* @param string $ownerView on GitHub (pinned to b608633a7e)
Solutions
- Move the removeSettingItem() call to a later stage, after items are guaranteed loaded — e.g. inside a backend request lifecycle event such as backend.page.beforeDisplay, not in register()/boot()
- Or trigger any code path that loads settings items first (render/extend the settings nav) before removing
- If you control the target plugin, deregister the item at its source (registerSettings()) instead of removing it from a third party
- Verify you are using the correct owner ('Author.Plugin') and code, since a wrong key on a loaded manager would silently no-op anyway
Example fix
// before — runs on every request, before settings are loaded
public function register()
{
SettingsManager::instance()->removeSettingItem('Author.Other', 'settings');
}
// after — defer until the backend has actually loaded settings items
public function boot(){
Event::listen('backend.page.beforeDisplay', function () {
SettingsManager::instance()->removeSettingItem('Author.Other', 'settings');
});
} Defensive patterns
Strategy: validation
Validate before calling
// Only remove once items exist — probe by requesting the item first
$manager = \System\Classes\SettingsManager::instance();
$itemKey = 'Author.Plugin' . '.' . 'setting';
// items are protected; defer via a backend event so the manager has loaded them
Event::listen('backend.page.beforeDisplay', function () use ($manager) {
$manager->removeSettingItem('Author.Plugin', 'setting');
}); Try / catch
try {
\System\Classes\SettingsManager::instance()->removeSettingItem('Author.Plugin', 'setting');
} catch (\System\Classes\SystemException $ex) {
// items not loaded yet — retry from a later lifecycle point instead of register()
} Prevention
- Never call manager mutation methods from register(); defer to boot() or a backend request event
- Prefer extending/deregistering at the source (registerSettings of the owning plugin) over third-party removal
- Wrap settings-manager calls in code paths that already render the backend
When it happens
Trigger: Calling SettingsManager::instance()->removeSettingItem('Author.Plugin', 'code') very early — typically from a plugin's register() or boot() on every request — before the settings manager has loaded its items (which normally happens when the backend settings UI or another consumer first asks for them).
Common situations: A plugin tries to hide another plugin's settings page and does it too early in the boot cycle; code ported from an older version where items happened to be preloaded; an artisan command that manipulates settings without ever triggering the backend load path.
Related errors
- Plugin configuration file plugin.yaml is not found for the p
- Too much recursion! Check for circular dependencies in your
- The resizer file ':name' is not found.
- The combiner file ':name' is not found.
- The resizer file ':name' is not found.
AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21).
Data as JSON: /api/errors/34a101472e6a0a19.
Report an issue: GitHub.