octobercms/october · error · ApplicationException

Unable to set active theme. Missing a site definition.

Error message

Unable to set active theme. Missing a site definition.

What it means

ApplicationException thrown by `Theme::setActiveTheme()` when the site lookup fails: `Site::getEditSite()` (backend context) or `Site::getPrimarySite()` (other contexts) returned null. Winter CMS stores the active theme on the site record, so without a site row the update target is unknown and the operation aborts before touching the database.

Source

Thrown at modules/cms/classes/Theme.php:237

        return self::$activeThemeCache = $theme;
    }

    /**
     * setActiveTheme sets the active theme
     *
     * The active theme code is stored in the database and overrides the
     * configuration cms.active_theme config item.
     */
    public static function setActiveTheme(string $code)
    {
        $theme = static::load($code);
        if ($theme->isLocked()) {
            throw new ApplicationException(Lang::get('cms::lang.theme.active.is_locked', ['theme' => $code]));
        }

        $site = App::runningInBackend() ? Site::getEditSite() : Site::getPrimarySite();
        if (!$site) {
            throw new ApplicationException(__("Unable to set active theme. Missing a site definition."));
        }

        Db::table($site->getTable())->where('id', $site->id)->update(['theme' => $code]);
        Config::set('cms.active_theme', $code);

        self::resetCache();

        /**
         * @event cms.theme.setActiveTheme
         * Fires when the active theme has been changed.
         *
         * Example usage:
         *
         *     Event::listen('cms.theme.setActiveTheme', function ($code) {
         *         \Log::info("Theme has been changed to $code");
         *     });
         *
         */

View on GitHub (pinned to b608633a7e)

Solutions

  1. Run the full setup/migrations: `php artisan winter:up` (or `php artisan migrate --seed`) so the primary site row exists.
  2. Verify with a tinker check: `Site::getPrimarySite()` should return a model, not null.
  3. If the site row was soft-deleted or corrupted, recreate it via the backend multi-site settings.

Example fix

# before — sites table empty, setActiveTheme() aborts
$ php artisan tinker
>>> Site::getPrimarySite()   # null

# after — create/migrate the site records
$ php artisan winter:up
>>> Site::getPrimarySite()   # returns the primary site model
Defensive patterns

Strategy: validation

Validate before calling

use System\Models\Site; // winter multi-site

$site = app()->runningInBackend() ? Site::getEditSite() : Site::getPrimarySite();
if (!$site) {
    return back()->with('error', 'No site record found. Run `php artisan winter:up` before changing the theme.');
}
Theme::setActiveTheme($code);

Try / catch

try {
    Theme::setActiveTheme($code);
} catch (ApplicationException $e) {
    if (str_contains($e->getMessage(), 'site definition')) {
        // DB not provisioned: point the operator at setup instead of failing silently
        Log::critical('setActiveTheme blocked: no site record');
        return redirect()->to('/backend/system/updates');
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling setActiveTheme() on an installation whose sites table is empty or missing — migrations never run, database truncated/restored without seed data, or a console command running where no primary site is defined.

Common situations: Partial deployments where `php artisan migrate` was skipped; staging databases copied without seed rows; custom installers that create config but not the primary site; multi-site plugins whose setup step was missed.

Related errors


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