{"record":{"id":"c56b6334ecdf5422","repo":"ratchetphp/Ratchet","slug":"handler-must-be-instance-of-sessionhandlerinterface-c56b63","errorCode":null,"errorMessage":"Handler must be instance of SessionHandlerInterface","messagePattern":"Handler must be instance of SessionHandlerInterface","errorType":"validation","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Ratchet/Session/Storage/VirtualSessionStorageForSymfony7.php","lineNumber":87,"sourceCode":"    public function save(): void {\n        // get the data from the bags?\n        // serialize the data\n        // save the data using the saveHandler\n//        $this->saveHandler->write($this->saveHandler->getId(),\n\n        if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {\n            $this->saveHandler->setActive(false);\n        }\n\n        $this->closed = true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setSaveHandler(AbstractProxy|\\SessionHandlerInterface|null $saveHandler): void {\n        if (!($saveHandler instanceof \\SessionHandlerInterface)) {\n            throw new \\InvalidArgumentException('Handler must be instance of SessionHandlerInterface');\n        }\n\n        if (!($saveHandler instanceof VirtualProxy)) {\n            $saveHandler = new VirtualProxy($saveHandler);\n        }\n\n        $this->saveHandler = $saveHandler;\n    }\n}\n","sourceCodeStart":69,"sourceCodeEnd":97,"githubUrl":"https://github.com/ratchetphp/Ratchet/blob/e621c6c40bf684bbbb877102416ad5303d05a9cc/src/Ratchet/Session/Storage/VirtualSessionStorageForSymfony7.php#L69-L97","documentation":"VirtualSessionStorageForSymfony7::setSaveHandler accepts AbstractProxy|\\SessionHandlerInterface|null but explicitly rejects any non-\\SessionHandlerInterface value (including Symfony AbstractProxy instances) with an InvalidArgumentException. The typed check exists because the handler must be wrappable in Ratchet's VirtualProxy for virtual sessions. Passing an AbstractProxy — accepted elsewhere in Symfony 7 session code — is deliberately rejected here.","triggerScenarios":"Calling setSaveHandler (directly or via __construct) with an Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy instance, null, or any object not implementing \\SessionHandlerInterface.","commonSituations":"Symfony 7 integrations where the framework hands an AbstractProxy to a session storage's setSaveHandler; code migrated from Symfony 6 passing proxy-wrapped handlers; DI misconfiguration supplying the wrong service.","solutions":["Unwrap the AbstractProxy and pass its underlying \\SessionHandlerInterface implementation instead.","Pass a plain \\SessionHandlerInterface implementation (e.g. new \\SessionHandler()) to the constructor.","If the handler comes from Symfony DI, configure the service definition to expose the raw handler implementing \\SessionHandlerInterface."],"exampleFix":"// before\n$storage->setSaveHandler($abstractProxy); // Symfony 7 AbstractProxy rejected\n// after\n$handler = method_exists($abstractProxy, 'getHandler') ? $abstractProxy->getHandler() : new \\SessionHandler();\nif ($handler instanceof \\SessionHandlerInterface) {\n    $storage->setSaveHandler($handler);\n}","handlingStrategy":"type-guard","validationCode":"if (!$handler instanceof \\SessionHandlerInterface) {\n    // Symfony 7 may hand an AbstractProxy; unwrap it\n    $handler = $handler instanceof \\Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy && method_exists($handler, 'getHandler')\n        ? $handler->getHandler()\n        : new \\SessionHandler();\n}\n$storage = new \\Ratchet\\Session\\Storage\\VirtualSessionStorageForSymfony7('sid', $handler);","typeGuard":"function isRawSessionHandler($h): bool { return $h instanceof \\SessionHandlerInterface; }","tryCatchPattern":"try {\n    $storage->setSaveHandler($handler);\n} catch (\\InvalidArgumentException $e) {\n    $storage->setSaveHandler(new \\SessionHandler());\n}","preventionTips":["Never pass Symfony AbstractProxy instances to Ratchet's Symfony7 storage — pass raw handlers.","After Symfony upgrades, re-check the type the framework supplies to setSaveHandler.","Wrap DI services so the container exposes \\SessionHandlerInterface implementations only."],"tags":["php","session","symfony7","invalid-argument"],"backgroundTag":"invalid-argument-value","analyzedSha":"e621c6c40bf684bbbb877102416ad5303d05a9cc","analyzedAt":"2026-09-16T00:13:27.878Z","contentChangedAt":"2026-09-16T00:13:27.878Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}