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

  1. 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
  2. If you must wrap a foreign loop, implement Workerman\Events\EventInterface on the wrapper (add, del, repeat, delay, deleteAllTimer, run, stop, onError, ...)
  3. 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

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


AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21). Data as JSON: /api/errors/41d653e34a220e1e. Report an issue: GitHub.