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

  1. Use real callables: closures, [new Decorator, 'method'], ['Class', 'staticMethod'], or invokable objects
  2. Validate first: $invalid = array_filter($extenders, fn ($e) => !is_callable($e)); and fail with your own message
  3. For config-driven extenders, map names to callables before calling setExtenders()
  4. 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

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


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