getgrav/grav · error · LogicException

User class was called too early!

Error message

User class was called too early!

What it means

Grav\Common\User\User is a shim whose actual parent class (Flex UserObject vs DataUser User) is chosen at file-load time from the GRAV_USER_INSTANCE constant, defined during Grav bootstrap from the accounts configuration. The guard at line 19 throws a LogicException if the file gets autoloaded before the constant exists, preventing the wrong class hierarchy from being compiled.

Source

Thrown at system/src/Grav/Common/User/User.php:19

<?php

/**
 * @package    Grav\Common\User
 *
 * @copyright  Copyright (c) 2015 - 2026 Trilby Media, LLC. All rights reserved.
 * @license    MIT License; see LICENSE file for details.
 */

namespace Grav\Common\User;

use Grav\Common\Grav;
use Grav\Common\User\DataUser;
use Grav\Common\Flex;
use Grav\Common\User\Interfaces\UserCollectionInterface;
use Grav\Common\User\Interfaces\UserInterface;

if (!defined('GRAV_USER_INSTANCE')) {
    throw new \LogicException('User class was called too early!');
}

if (defined('GRAV_USER_INSTANCE') && GRAV_USER_INSTANCE === 'FLEX') {
    /**
     * @deprecated 1.6 Use $grav['accounts'] instead of static calls. In type hints, please use UserInterface.
     */
    class User extends Flex\Types\Users\UserObject
    {
        /**
         * Load user account.
         *
         * Always creates user object. To check if user exists, use $this->exists().
         *
         * @param string $username
         * @return UserInterface
         * @deprecated 1.6 Use $grav['accounts']->load(...) instead.
         */
        public static function load($username)

View on GitHub (pinned to 6040efed04)

Solutions

  1. Do not touch the User shim directly — use $grav['accounts'] (UserCollectionInterface) or type-hint UserInterface after Grav is booted.
  2. In scripts/tests, run the standard Grav bootstrap (as index.php / bin/grav do) so GRAV_USER_INSTANCE is defined before the class is first loaded.
  3. Move logic that needs user classes into plugin hooks (onPluginsInitialized or later) instead of autoload-time code.

Example fix

// before (standalone script)
require 'vendor/autoload.php';
$user = \Grav\Common\User\User::load('joe'); // LogicException: called too early

// after
require 'vendor/autoload.php';
// boot Grav exactly like index.php / bin/grav do (loader + container + init)
$grav = \Grav\Common\Grav::instance(['loader' => require 'vendor/autoload.php']);
// ... bootstrap ...
$user = $grav['accounts']->load('joe');
Defensive patterns

Strategy: validation

Validate before calling

if (!defined('GRAV_USER_INSTANCE')) {
    // Grav is not bootstrapped: boot it (see index.php) or exit with a clear message
    fwrite(STDERR, "Boot Grav before using user classes.\n");
    exit(1);
}
$class = \Grav\Common\User\User::class;

Type guard

function gravIsBootstrapped(): bool
{
    return defined('GRAV_USER_INSTANCE');
}

Try / catch

try {
    $user = \Grav\Common\User\User::load('joe');
} catch (\LogicException $e) {
    // class shim loaded pre-boot: bootstrap Grav and retry, or fix call ordering
}

Prevention

When it happens

Trigger: Referencing Grav\Common\User\User (User::load(), instanceof, static calls) from code that runs with the autoloader but without a booted Grav: standalone CLI scripts that just require vendor/autoload.php, PHPUnit tests, or logic triggered during composer autoload-time events.

Common situations: Unit tests requiring the autoloader but never booting Grav; custom entry points using grav's vendor/autoload.php directly; plugin code executed outside the normal Grav request lifecycle.

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/afaba462d9455d0d. Report an issue: GitHub.