phalcon/cphalcon · error · Phalcon\Container\Exceptions\InvalidExtender
Extender at key '{key}' for service '{service}' is not calla
Error message
Extender at key '{key}' for service '{service}' is not callable What it means
setExtenders() stores an array of decorators applied after a service is built. It validates every element with is_callable() and throws InvalidExtender on the first non-callable, including the offending array key in the message. Extend callables receive ($instance, $container).
Source
Thrown at phalcon/Container/Definition/ServiceDefinition.zep:435
/**
* Set extenders
*
* @param array<array-key, callable> $extenders
*
* @return static
* @throws FrozenDefinition
* @throws InvalidExtender
*/
public function setExtenders(array extenders) -> <static>
{
var extender, key;
this->checkFrozen();
for key, extender in extenders {
if (!is_callable(extender)) {
throw new InvalidExtender(this->serviceName, (string) key);
}
}
let this->extenders = extenders;
return this;
}
/**
* Set a factory
*
* @param callable $factory
*
* @return static
* @throws FrozenDefinition
*/
public function setFactory(callable factory) -> <static>
{View on GitHub (pinned to b7419de9cd)
Solutions
- Use real callables: closures, [new Decorator, 'method'], ['Class', 'staticMethod'], or invokable objects
- Validate first: $invalid = array_filter($extenders, fn ($e) => !is_callable($e)); and fail with your own message
- For config-driven extenders, map names to callables before calling setExtenders()
- Use the '{key}' from the message to locate exactly which array entry failed
Example fix
// before $def->setExtenders(['decorate']); // InvalidExtender at key 0 // after $def->setExtenders([[new Decorator(), 'decorate']]);
Defensive patterns
Strategy: validation
Validate before calling
$extenders = array_map(
fn (string $method) => [new Decorator(), $method],
$config->extenders
);
$invalid = array_keys(array_filter($extenders, fn ($e) => !is_callable($e)));
if ($invalid !== []) {
throw new LogicException('Non-callable extenders at keys: ' . implode(', ', $invalid));
}
$def->setExtenders($extenders); Type guard
function allCallable(array $extenders): bool
{
return array_reduce($extenders, fn ($ok, $e) => $ok && is_callable($e), true);
} Try / catch
use Phalcon\Container\Exceptions\InvalidExtender;
try {
$def->setExtenders($extenders);
} catch (InvalidExtender $e) {
// message contains the failing key; fix that entry and re-set
} Prevention
- Build extender arrays only from verified callables (closures, [object, method], invokable objects)
- Never load raw method-name strings from config as extenders — map them to callables first
- Remember extender signature is ($instance, $container)
When it happens
Trigger: Passing plain strings that are not function names ('decorate'); method-name strings without an object; arrays with wrong shape; passing an instance instead of a callable; extender lists loaded from config where values are method names rather than callables.
Common situations: Config-driven decoration lists where each entry is a method name string; serialized extender arrays; typos in function names; copying extend() closures into setExtenders() arrays incorrectly.
Related errors
- Service '{name}' not found
- Instance '{name}' not found
- Parameter '{name}' not found
- Service '{name}' not registered
- Circular alias detected: '{alias}'
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/b36c74e979f9cbac.
Report an issue: GitHub.