walkor/workerman · critical · RuntimeException
${errMsg}
Error message
${errMsg} What it means
During Worker->run() the listening socket is created with stream_socket_server() over the configured listen address (tcp://host:port, unix://path). On failure Workerman throws the OS-provided $errMsg verbatim - typical texts are 'Address already in use', 'Permission denied', 'Cannot assign requested address'. This is the classic bind/listen failure and always occurs at worker startup before the event loop runs.
Source
Thrown at src/Worker.php:2462
}
if (!$this->mainSocket) {
$localSocket = $this->parseSocketAddress();
// Flag.
$flags = $this->transport === 'udp' ? STREAM_SERVER_BIND : STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
$errNo = 0;
$errMsg = '';
// SO_REUSEPORT.
if ($this->reusePort && DIRECTORY_SEPARATOR !== '\\') {
stream_context_set_option($this->socketContext, 'socket', 'so_reuseport', 1);
}
// Create an Internet or Unix domain server socket.
$this->mainSocket = stream_socket_server($localSocket, $errNo, $errMsg, $flags, $this->socketContext);
if (!$this->mainSocket) {
throw new RuntimeException($errMsg);
}
if ($this->transport === 'ssl') {
stream_socket_enable_crypto($this->mainSocket, false);
} elseif ($this->transport === 'unix') {
$socketFile = substr($localSocket, 7);
if ($this->user) {
chown($socketFile, $this->user);
}
if ($this->group) {
chgrp($socketFile, $this->group);
}
}
// Try to open keepalive for tcp and disable Nagle algorithm.
if (function_exists('socket_import_stream') && self::BUILD_IN_TRANSPORTS[$this->transport] === 'tcp') {
set_error_handler(static fn (): bool => true);
$socket = socket_import_stream($this->mainSocket);View on GitHub (pinned to 1391112a61)
Solutions
- Find and stop the occupant: 'ss -ltnp | grep :PORT' (or lsof -i :PORT), kill it or enable reusePort ('Worker->reusePort = true') when running multiple instances intentionally
- For ports below 1024 run with privileges, use setcap, or put a reverse proxy in front and listen on a high port
- Remove a stale unix socket before start (rm /path/app.sock) or add unlink logic in onWorkerStart; fix the listen string to a valid address your host actually owns
Example fix
# before php start.php start # RuntimeException: Address already in use # after ss -ltnp | grep :8080 # find old master kill <old-master-pid> # or: php start.php stop php start.php start # for intentional multi-instance listeners: # $worker->reusePort = true;
Defensive patterns
Strategy: validation
Validate before calling
$port = 8080;
$probe = @fsockopen('127.0.0.1', $port, $errNo, $errStr, 1);
if ($probe !== false) {
fclose($probe);
throw new RuntimeException("port $port already in use - stop the old instance or enable reusePort");
}
if ($port < 1024 && (function_exists('posix_getuid') ? posix_getuid() : 0) !== 0) {
throw new RuntimeException("port $port needs privileges or a reverse proxy");
} Try / catch
try {
$worker = new Worker('http://0.0.0.0:8080');
$worker->reusePort = true; // allow parallel listeners
Worker::runAll();
} catch (RuntimeException $e) {
// message is the OS bind error, e.g. 'Address already in use'
fwrite(STDERR, 'listen failed: ' . $e->getMessage() . PHP_EOL);
exit(1);
} Prevention
- Always stop the old instance before starting (php start.php stop) or enable reusePort for multi-instance setups
- For unix transports, unlink the stale socket file in onWorkerStart or a pre-start script
- Bind ports >= 1024 for non-root services and front them with nginx/caddy
When it happens
Trigger: Another process holds the port (previous instance not stopped, dev copy running); binding a port < 1024 as non-root; listen address referencing an IPv6 address on a host without IPv6 or a non-local IP; unix socket file already exists (stale socket from a crashed run); SELinux/AppArmor denying the bind.
Common situations: Starting the service twice; crash left a stale unix socket file; deploying as non-root on port 80/443; misconfigured 0.0.0.0 vs specific interface addresses; SELinux-enabled hosts blocking non-standard ports for the service context.
Related errors
- ${msg}
- Bad worker->transport ${var_export($this->transport, true)}
- Bad remoteAddress
- Invalid protocol scheme '$scheme'
- class \Protocols\$scheme not exist
AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21).
Data as JSON: /api/errors/70fa913c24e313f1.
Report an issue: GitHub.