walkor/workerman · critical · RuntimeException

${msg}

Error message

${msg}

What it means

Workerman runs Worker::checkPortAvailable() on Linux while the master process is in STATUS_STARTING: for every tcp (non-unix, non-udp) listener it does a probe stream_socket_server() bind and installs a set_error_handler that converts the PHP warning into this RuntimeException. So the message is the raw stream_socket_server warning, e.g. 'stream_socket_server(): unable to connect to tcp://0.0.0.0:8080 (Address already in use)'. The library throws it to fail fast, before forking worker processes, when the address cannot be bound.

Solutions

  1. Find and stop the process holding the port: `ss -ltnp 'sport = :8080'` or `lsof -i :8080`, then `php start.php stop` (or kill the PID) and start again.
  2. If the other listener is intentional and you want several instances sharing the port, set `$worker->reusePort = true;` on Linux (SO_REUSEPORT).
  3. Change to a free port in the Worker constructor, e.g. `new Worker('http://0.0.0.0:8081')`.
  4. For ports below 1024, run with adequate privilege or grant the PHP binary the capability once: `sudo setcap 'cap_net_bind_service=+ep' $(which php)`.
  5. If the host part is wrong (typo like 0.0.0.1 or an IP not on the machine), correct it to a local address such as 127.0.0.1 or 0.0.0.0.

Example fix

// before: second instance while the first still holds the port
$worker = new Worker('http://0.0.0.0:8080');
Worker::runAll(); // RuntimeException: stream_socket_server(): unable to connect to tcp://0.0.0.0:8080 (Address already in use)

// after: stop the old instance first, or opt into port sharing
$worker = new Worker('http://0.0.0.0:8080');
$worker->reusePort = true; // SO_REUSEPORT, allows multiple listeners on Linux
Worker::runAll();
Defensive patterns

Strategy: validation

Validate before calling

// Probe the port before handing control to Workerman
$listen = '0.0.0.0:8080';
$probe = @stream_socket_server("tcp://$listen", $errno, $errstr);
if ($probe === false) {
    fwrite(STDERR, "Cannot bind $listen: $errstr\n");
    exit(1);
}
fclose($probe); // release it so Workerman can bind

$worker = new Worker("http://$listen");
Worker::runAll();

Try / catch

try {
    Worker::runAll();
} catch (RuntimeException $e) {
    if (str_contains($e->getMessage(), 'Address already in use')) {
        // port conflict: report PID hint and exit non-zero
        fwrite(STDERR, $e->getMessage() . "\nRun: php start.php stop\n");
        exit(1);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Running `php start.php start` (the runAll path sets STATUS_STARTING) on Linux when: another process (a previous Workerman instance, nginx, apache, a dev server) already listens on the same host:port; the port is <1024 and the process is not root/cap_net_bind_service; the host part of listen resolves to an address not available on the machine; or the address is otherwise invalid. Only workers with transport==='tcp' and a socketName not starting with 'unix'/'udp' are probed.

Common situations: Starting a second copy of the server while the old one is still alive (kill -9 left the socket held, or the stop command was skipped); deploying to a container/port mapping that collides with another service; switching to port 80/443 without privileges; porting a config that worked on macOS/Windows (the probe only runs when DIRECTORY_SEPARATOR === '/').

Related errors


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

Appendix: source

Thrown at src/Worker.php:2536

     *
     * @return void
     */
    protected static function checkPortAvailable(): void
    {
        foreach (static::$workers as $worker) {
            $socketName = $worker->getSocketName();
            if (DIRECTORY_SEPARATOR === '/'  // if linux
                && static::$status === static::STATUS_STARTING // only for starting status
                && $worker->transport === 'tcp' // if tcp socket
                && !str_starts_with($socketName, 'unix') // if not unix socket
                && !str_starts_with($socketName, 'udp')) { // if not udp socket

                $address = parse_url($socketName);
                if (isset($address['host']) && isset($address['port'])) {
                    $address = "tcp://{$address['host']}:{$address['port']}";
                    $server = null;
                    set_error_handler(function ($code, $msg) {
                        throw new RuntimeException($msg);
                    });
                    $server = stream_socket_server($address, $code, $msg);
                    if ($server) {
                        fclose($server);
                    }
                    restore_error_handler();
                }
            }
        }
    }

    /**
     * Parse local socket address.
     */
    protected function parseSocketAddress(): ?string
    {
        if (!$this->socketName) {
            return null;

View on GitHub (pinned to 1391112a61)