walkor/workerman · error · RuntimeException
%s::$eventLoopClass must implement %s
Error message
%s::$eventLoopClass must implement %s
What it means
Before the master process creates its event loop, Worker::initGlobalEvent() validates the configured Worker::$eventLoopClass and requires it to implement Workerman\Events\EventInterface (add/del/loop/onError/repeat/delay/...). Any other class - even a working event loop from another ecosystem - is rejected because Workerman drives it through that interface.
Source
Thrown at src/Worker.php:800
restore_error_handler();
}
/**
* Init global event.
*
* @return void
*/
protected static function initGlobalEvent(): void
{
if (static::$globalEvent !== null) {
static::$eventLoopClass = get_class(static::$globalEvent);
static::$globalEvent = null;
return;
}
if (!empty(static::$eventLoopClass)) {
if (!is_subclass_of(static::$eventLoopClass, EventInterface::class)) {
throw new RuntimeException(sprintf('%s::$eventLoopClass must implement %s', static::class, EventInterface::class));
}
return;
}
static::$eventLoopClass = match (true) {
extension_loaded('event') => Event::class,
default => Select::class,
};
}
/**
* Lock.
*
* @param int $flag
* @return void
*/
protected static function lock(int $flag = LOCK_EX): void
{View on GitHub (pinned to 1391112a61)
Solutions
- Use one of Workerman's own loops, e.g. Workerman\Events\Event (ext-event), Workerman\Events\Select, or the Fiber/Ev implementations shipped in src/Events
- If you must wrap a foreign loop, implement Workerman\Events\EventInterface on the wrapper (add, del, repeat, delay, deleteAllTimer, run, stop, onError, ...)
- Fix the typo in the class name or unset Worker::$eventLoopClass to fall back to the automatic selection (ext-event if present, else Select)
Example fix
// before Worker::$eventLoopClass = React\EventLoop\StreamSelectLoop::class; Worker::runAll(); // throws '$eventLoopClass must implement Workerman\Events\EventInterface' // after Worker::$eventLoopClass = Workerman\Events\Event::class; // or omit for auto-select Worker::runAll();
Defensive patterns
Strategy: validation
Validate before calling
$loopClass = $config['eventLoopClass'] ?? Workerman\Events\Select::class;
if (!is_subclass_of($loopClass, Workerman\Events\EventInterface::class)) {
throw new InvalidArgumentException("$loopClass does not implement EventInterface");
}
Worker::$eventLoopClass = $loopClass; Type guard
function isValidLoopClass(string $class): bool
{
return is_subclass_of($class, Workerman\Events\EventInterface::class);
} Prevention
- Only assign loop classes from Workerman\Events\* unless you wrote an EventInterface adapter yourself
- Add a boot-time assertion for the configured loop class so misconfigurations fail before forking
When it happens
Trigger: Worker::$eventLoopClass = React\EventLoop\StreamSelectLoop::class or an Amp loop (no EventInterface); a typo in a configured class name that resolves to an unrelated class; a custom loop wrapper that forgot to implement the interface; config pulled from env applied without validation.
Common situations: Trying to share an existing ReactPHP/Swoole-style loop with Workerman; upgrading Workerman major versions where EventInterface changed; custom event-loop wrappers maintained in app code that drifted from the interface.
Related errors
- The $outputStream must to be a stream, %s given
- Event::addTimer($delay) failed
- Event::addTimer($interval) failed
- Bad remoteAddress
- Invalid protocol scheme '$scheme'
AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21).
Data as JSON: /api/errors/41d653e34a220e1e.
Report an issue: GitHub.