{"record":{"id":"a588eecc08631fb2","repo":"getgrav/grav","slug":"service-definition-is-not-a-closure-or-invokable-o","errorCode":null,"errorMessage":"Service definition is not a Closure or invokable object.","messagePattern":"Service definition is not a Closure or invokable object\\.","errorType":"exception","errorClass":"Pimple\\Exception\\ExpectedInvokableException","httpStatus":null,"severity":"error","filePath":"system/src/Pimple/Container.php","lineNumber":214,"sourceCode":"            }\n\n            unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]);\n        }\n    }\n\n    /**\n     * Marks a callable as being a factory service.\n     *\n     * @param callable $callable A service definition to be used as a factory\n     *\n     * @return callable The passed callable\n     *\n     * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object\n     */\n    public function factory(object $callable): object\n    {\n        if (!method_exists($callable, '__invoke')) {\n            throw new ExpectedInvokableException('Service definition is not a Closure or invokable object.');\n        }\n\n        $this->factories->attach($callable);\n\n        return $callable;\n    }\n\n    /**\n     * Protects a callable from being interpreted as a service.\n     *\n     * This is useful when you want to store a callable as a parameter.\n     *\n     * @param callable $callable A callable to protect from being evaluated\n     *\n     * @return callable The passed callable\n     *\n     * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object\n     */","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Pimple/Container.php#L196-L232","documentation":"Container::factory() marks a service definition so every retrieval invokes it again instead of caching and freezing the result. Pimple tracks definitions in SplObjectStorage, so the definition must be a Closure or another object with __invoke(); a plain object without __invoke() throws ExpectedInvokableException.","triggerScenarios":"Calling $container->factory(new SomeFactory()) where SomeFactory has no __invoke() method, or passing an object that is merely an instantiated service rather than a factory definition. String and array callables fail the object parameter type before reaching this check.","commonSituations":"Confusing the factory definition with the product it creates, migrating callable code from another container that accepts first-class callable strings, or decorating a class without adding an invoker.","solutions":["Wrap construction in a closure: $container['service'] = $container->factory(fn (Container $c) => new SomeFactory(...));","Make the passed object invokable by implementing __invoke(Container $container).","Do not call factory() on an already constructed service; assign it as a normal value if no invocation is wanted."],"exampleFix":"// before\n$container['report'] = $container->factory(new ReportBuilder()); // ReportBuilder has no __invoke\n\n// after\n$container['report'] = $container->factory(\n    fn (Container $c) => new ReportBuilder($c['logger'])\n);","handlingStrategy":"type-guard","validationCode":"if (!is_object($definition) || !method_exists($definition, '__invoke')) {\n    throw new InvalidArgumentException('A factory definition must be a Closure or invokable object.');\n}\n$definition = $container->factory($definition);","typeGuard":"function isInvokableDefinition(mixed $definition): bool\n{\n    return is_object($definition) && method_exists($definition, '__invoke');\n}","tryCatchPattern":"try {\n    $container->factory($definition);\n} catch (ExpectedInvokableException $e) {\n    throw new InvalidArgumentException('Wrap object construction in a closure before marking it as a factory.', 0, $e);\n}","preventionTips":["Use fn (Container $c) => new Service(...) as the factory shape.","Do not pass instantiated services to factory().","Add tests that resolve every registered service twice when factory semantics matter.","Document whether a definition is shared, protected, or a factory."],"tags":["pimple","dependency-injection","php","factory","invokable","type-error"],"backgroundTag":"non-invokable-service-definition","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}