ratchetphp/Ratchet · error · InvalidArgumentException

Handler must be instance of SessionHandlerInterface

Error message

Handler must be instance of SessionHandlerInterface

What it means

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.

Solutions

  1. Unwrap the AbstractProxy and pass its underlying \SessionHandlerInterface implementation instead.
  2. Pass a plain \SessionHandlerInterface implementation (e.g. new \SessionHandler()) to the constructor.
  3. If the handler comes from Symfony DI, configure the service definition to expose the raw handler implementing \SessionHandlerInterface.

Example fix

// before
$storage->setSaveHandler($abstractProxy); // Symfony 7 AbstractProxy rejected
// after
$handler = method_exists($abstractProxy, 'getHandler') ? $abstractProxy->getHandler() : new \SessionHandler();
if ($handler instanceof \SessionHandlerInterface) {
    $storage->setSaveHandler($handler);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!$handler instanceof \SessionHandlerInterface) {
    // Symfony 7 may hand an AbstractProxy; unwrap it
    $handler = $handler instanceof \Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy && method_exists($handler, 'getHandler')
        ? $handler->getHandler()
        : new \SessionHandler();
}
$storage = new \Ratchet\Session\Storage\VirtualSessionStorageForSymfony7('sid', $handler);

Type guard

function isRawSessionHandler($h): bool { return $h instanceof \SessionHandlerInterface; }

Try / catch

try {
    $storage->setSaveHandler($handler);
} catch (\InvalidArgumentException $e) {
    $storage->setSaveHandler(new \SessionHandler());
}

Prevention

When it happens

Trigger: Calling setSaveHandler (directly or via __construct) with an Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy instance, null, or any object not implementing \SessionHandlerInterface.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of ratchetphp/Ratchet@e621c6c40b (2026-09-16). Data as JSON: /api/errors/c56b6334ecdf5422. Report an issue: GitHub.

Appendix: source

Thrown at src/Ratchet/Session/Storage/VirtualSessionStorageForSymfony7.php:87

    public function save(): void {
        // get the data from the bags?
        // serialize the data
        // save the data using the saveHandler
//        $this->saveHandler->write($this->saveHandler->getId(),

        if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
            $this->saveHandler->setActive(false);
        }

        $this->closed = true;
    }

    /**
     * {@inheritdoc}
     */
    public function setSaveHandler(AbstractProxy|\SessionHandlerInterface|null $saveHandler): void {
        if (!($saveHandler instanceof \SessionHandlerInterface)) {
            throw new \InvalidArgumentException('Handler must be instance of SessionHandlerInterface');
        }

        if (!($saveHandler instanceof VirtualProxy)) {
            $saveHandler = new VirtualProxy($saveHandler);
        }

        $this->saveHandler = $saveHandler;
    }
}

View on GitHub (pinned to e621c6c40b)