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
- Use a Closure: '.volt' => function ($view, $di) { $volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di); ... return $volt; }
- Or a service name string that the DI can resolve: '.volt' => 'voltEngine'
- Or an already-constructed engine instance
- 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
- Register engines as Closures or DI service-name strings only
- Validate config-derived engine maps before registerEngines()
- Do not pass arrays/objects-as-config into engine slots
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
- Views directory must be a string or an array
- A dependency injection container is required to access appli
- Macro '{name}' was called without parameter: {variableName}
- To use 'array' adapter you have to specify the 'config' as a
- Configuration file {fileName} cannot be loaded
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/96ddfb0dac57ef57.
Report an issue: GitHub.