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 $owner

View on GitHub (pinned to b608633a7e)

Solutions

  1. 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()
  2. Or trigger any code path that loads settings items first (render/extend the settings nav) before removing
  3. If you control the target plugin, deregister the item at its source (registerSettings()) instead of removing it from a third party
  4. 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

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


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/34a101472e6a0a19. Report an issue: GitHub.