phalcon/cphalcon · error · Phalcon\Cli\Console\Exceptions\InvalidModuleDefinition

Invalid module definition for module '{moduleName}': The mod

Error message

Invalid module definition for module '{moduleName}': The module definition object must be a Closure

What it means

The other branch of CLI module loading: when the definition is an object (not an array), Phalcon requires it to be a Closure. The closure is invoked with the DI container via call_user_func_array and must return an object implementing the module definition interface (registerAutoloaders/registerServices). Passing any other object — even an already-constructed module instance — throws this.

Source

Thrown at phalcon/Cli/Console.zep:145

                    if !class_exists(className, false) {
                        require_once path;
                    }
                }

                let moduleObject = <ModuleDefinitionInterface> this->container->get(className);

                /**
                 * 'registerAutoloaders' and 'registerServices' are
                 * automatically called
                 */
                moduleObject->registerAutoloaders(this->container);
                moduleObject->registerServices(this->container);
            } else {
                /**
                 * A module definition object, can be a Closure instance
                 */
                if unlikely !(module instanceof Closure) {
                    throw new InvalidModuleDefinition(
                        moduleName,
                        "The module definition object must be a Closure"
                    );
                }

                let moduleObject = call_user_func_array(
                    module,
                    [
                        this->container
                    ]
                );
            }

            /**
             * The "afterStartModule" event is fired once the module has
             * started. Unlike Phalcon\Mvc\Application - where the return value
             * is a notification only - Console honors a `false` return and
             * aborts handling. This divergence is retained for backward

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Wrap construction in a closure: 'api' => function ($di) { return new \App\Api\Module($di->get('logger')); }
  2. Or switch to the array definition with className/path and let the DI container construct the module

Example fix

// before
$console->registerModules([
    'api' => new \App\Api\Module($logger),
]);

// after
$console->registerModules([
    'api' => function ($di) {
        return new \App\Api\Module($di->get('logger'));
    },
]);
Defensive patterns

Strategy: validation

Validate before calling

foreach ($modules as $name => $definition) {
    if (is_object($definition) && !$definition instanceof \Closure) {
        throw new InvalidArgumentException(sprintf(
            'Module "%s" object definition must be a Closure, got %s',
            $name,
            get_class($definition)
        ));
    }
}

Type guard

/**
 * Object module definitions must be Closures; anything else
 * (including module instances) is rejected by Cli\Console::handle().
 */
function isClosureModuleDefinition($definition): bool
{
    return $definition instanceof \Closure;
}

Try / catch

try {
    $console->handle();
} catch (\Phalcon\Cli\Console\Exception\InvalidModuleDefinition $e) {
    if (strpos($e->getMessage(), 'Closure') !== false) {
        // Object definition branch: wrap the instance in a closure instead
        $stderr->writeln('Wrap object module definitions in a Closure: ' . $e->getMessage());
    }
    exit(1);
}

Prevention

When it happens

Trigger: $console->registerModules(['api' => new \App\Api\Module($logger)]) — a ready-made instance instead of a Closure; also any stdClass or helper object used as the definition.

Common situations: Developers instantiate the module class directly because it needs constructor arguments, instead of wrapping construction in a closure or using the className/path array form.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/1877ab7de3659a58. Report an issue: GitHub.