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.
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)
Solutions
- 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.
- If the other listener is intentional and you want several instances sharing the port, set `$worker->reusePort = true;` on Linux (SO_REUSEPORT).
- Change to a free port in the Worker constructor, e.g. `new Worker('http://0.0.0.0:8081')`.
- 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)`.
- 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
- Always stop with `php start.php stop`/`reload` instead of kill -9 so the master releases ports cleanly.
- In containers/CI, assert the port is free (or use SO_REUSEPORT via $worker->reusePort on Linux) before start.
- Keep ports per-environment in config and reserve them so two services never claim the same one.
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
- ${errMsg}
- Bad remoteAddress
- Invalid protocol scheme '$scheme'
- class \Protocols\$scheme not exist
- Invalid protocol scheme '$scheme'
AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21).
Data as JSON: /api/errors/f2e8f1f51cf1a3a2.
Report an issue: GitHub.