phalcon/cphalcon · error · Phalcon\Mvc\View\Exceptions\InvalidEngineRegistration

Invalid template engine registration for extension: {extensi

Error message

Invalid template engine registration for extension: {extension}

What it means

Each registered engine entry may be an object (typically a Closure receiving the view, or a pre-built engine instance) or a string naming a DI service resolved via di->get($engineService, [$view]) — anything else (int, null, array, bool) throws InvalidEngineRegistration at phalcon/Mvc/View.zep:1334, because there is no way to turn that value into a rendering engine.

Source

Thrown at phalcon/Mvc/View.zep:1334

                        if engineService instanceof Closure {
                            let engineService = Closure::bind(
                                engineService,
                                di
                            );

                            let engines[extension] = call_user_func(
                                engineService,
                                this
                            );
                        } else {
                            let engines[extension] = engineService;
                        }
                    } else {
                        /**
                         * Engine can be a string representing a service in the DI
                         */
                        if typeof engineService != "string" {
                            throw new InvalidEngineRegistration(extension);
                        }

                        let engines[extension] = di->get(
                            engineService,
                            [this]
                        );
                    }
                }
            }

            let this->engines = engines;
        }

        return engines;
    }
}

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Use a Closure: '.volt' => function ($view, $di) { $volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di); ... return $volt; }
  2. Or a service name string that the DI can resolve: '.volt' => 'voltEngine'
  3. Or an already-constructed engine instance
  4. array_map/validation to assert every entry is_object() || is_string() before registerEngines()

Example fix

// before
$view->registerEngines(['.volt' => ['service' => 'voltEngine']]);

// after
$view->registerEngines(['.volt' => function ($view, $di) {
    $volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
    $volt->setOptions(['path' => cachePath('volt/')]);
    return $volt;
}]);
Defensive patterns

Strategy: validation

Validate before calling

foreach ($engines as $ext => $def) {
    if (!is_string($ext) || (!is_object($def) && !is_string($def))) {
        throw new \InvalidArgumentException("Invalid engine registration for {$ext}");
    }
}

Type guard

function isValidEngineMap(array $engines): bool
{
    foreach ($engines as $ext => $def) {
        if (!is_string($ext) || (!is_object($def) && !is_string($def))) {
            return false;
        }
    }
    return true;
}

Prevention

When it happens

Trigger: registerEngines(['.volt' => null]) from an unset variable; registerEngines(['.phtml' => ['class' => PhpEngine::class]]) passing a config array instead of a string or closure; entries sourced from decoded JSON config with wrong types.

Common situations: Copy-pasting engine config from documentation into an array-shaped config file and passing it verbatim; conditional engine registration where one branch leaves null; PHP 8.1+ deprecation-free refactors that changed closure formatting.

Related errors


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