ratchetphp/Ratchet · error · UnexpectedValueException
All routes must implement Ratchet\Http\HttpServerInterface
Error message
All routes must implement Ratchet\Http\HttpServerInterface
What it means
After the Router matches the incoming request's host/path to a route and instantiates the controller (either the object registered with the router or a string class name that it news up), it verifies the controller implements Ratchet\Http\HttpServerInterface. If not, it throws UnexpectedValueException, because every routed endpoint must expose onOpen/onMessage/onClose/onError to receive the connection. This protects against registering arbitrary classes or string class names of the wrong type as routes.
Solutions
- Make the controller class implement Ratchet\Http\HttpServerInterface (e.g. extend Ratchet\Http\HttpServer or implement onOpen/onMessage/onClose/onError with RequestInterface in onOpen).
- If you want WebSocket semantics at that route, wrap the handler in Ratchet\WebSocket\WsServer, which itself implements HttpServerInterface, before registering it.
- Double-check the string class name registered for the route; class_exists() alone passes, so confirm the resolved class is the intended HTTP handler.
- If the handler only needs HTTP request/response semantics, extend Ratchet\Http\HttpServer and override onOpen to return a ResponseInterface instead of expecting message streaming.
Example fix
// before
$router->addRoute('GET', '/ws', ChatHandler::class); // ChatHandler implements MessageComponentInterface only
// after
use Ratchet\WebSocket\WsServer;
$router->addRoute('GET', '/ws', new WsServer(new ChatHandler())); // WsServer implements HttpServerInterface Defensive patterns
Strategy: validation
Validate before calling
use Ratchet\Http\HttpServerInterface;
$controller = is_string($route['_controller']) ? new $route['_controller'] : $route['_controller'];
if (!$controller instanceof HttpServerInterface) {
throw new \LogicException(get_class($controller) . ' must implement HttpServerInterface before being routed');
} Type guard
function isRoutableHandler($handler): bool {
return $handler instanceof \Ratchet\Http\HttpServerInterface;
} Try / catch
try {
$router->onOpen($conn, $request);
} catch (\UnexpectedValueException $e) {
if (str_contains($e->getMessage(), 'HttpServerInterface')) {
error_log('Route controller does not implement HttpServerInterface: ' . $e->getMessage());
} else { throw $e; }
} Prevention
- Every class registered as a route target should extend Ratchet\Http\HttpServer or wrap its handler in WsServer.
- Add a boot-time smoke test that news up each route controller and asserts instanceof HttpServerInterface.
- Avoid string class names in routes when possible; register instances so type errors surface at wiring time.
- When reusing MessageComponentInterface handlers, always wrap them in WsServer before adding the route.
When it happens
Trigger: Registering a route whose controller is a plain class or closure that does not implement HttpServerInterface; registering a controller as a string class name (e.g. 'App\Handler') where the class exists but only implements MessageComponentInterface or nothing at all; a typo causing Router to resolve the wrong controller entry from the route matcher's output.
Common situations: Migrating an app from Ratchet WsServer-style handlers to HTTP routes while reusing old MessageComponentInterface controllers; defining a route handler that expects FastRoute-style callables; refactoring that renamed/moved the controller class behind an interface that is no longer HttpServerInterface.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- $request can not be null
- Invalid domain
- Invalid Port
- Invalid site control set
- Argument #4 ($loop) expected…
AI-assisted analysis of ratchetphp/Ratchet@e621c6c40b (2026-09-16).
Data as JSON: /api/errors/e6763331dd567410.
Report an issue: GitHub.
Appendix: source
Thrown at src/Ratchet/Http/Router.php:56
$context = $this->_matcher->getContext();
$context->setMethod($request->getMethod());
$context->setHost($uri->getHost());
try {
$route = $this->_matcher->match($uri->getPath());
} catch (MethodNotAllowedException $nae) {
return $this->close($conn, 405, array('Allow' => $nae->getAllowedMethods()));
} catch (ResourceNotFoundException $nfe) {
return $this->close($conn, 404);
}
if (is_string($route['_controller']) && class_exists($route['_controller'])) {
$route['_controller'] = new $route['_controller'];
}
if (!($route['_controller'] instanceof HttpServerInterface)) {
throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface');
}
$parameters = [];
foreach($route as $key => $value) {
if ((is_string($key)) && ('_' !== substr($key, 0, 1))) {
$parameters[$key] = $value;
}
}
$parameters = array_merge($parameters, Query::parse($uri->getQuery() ?: ''));
$request = $request->withUri($uri->withQuery(Query::build($parameters)));
$conn->controller = $route['_controller'];
$conn->controller->onOpen($conn, $request);
}
/**
* {@inheritdoc}View on GitHub (pinned to e621c6c40b)