walkor/workerman · error · RuntimeException

class \Protocols\$scheme not exist

Error message

class \Protocols\$scheme not exist

What it means

The scheme was alphanumeric but is not a built-in transport, so Workerman tried to load an application-layer protocol class: first '\Protocols\<Scheme>' then '\Workerman\Protocols\<Scheme>'. Neither could be autoloaded, so the constructor aborts. This is how workerman resolves schemes like http, ws, text, frame and any user-defined protocol.

Source

Thrown at src/Connection/AsyncTcpConnection.php:222

                : $this->remoteHost . ':' . $this->remotePort;
        }

        $this->id = $this->realId = self::$idRecorder++;
        if (PHP_INT_MAX === self::$idRecorder) {
            self::$idRecorder = 0;
        }
        // Check application layer protocol class.
        if (!isset(self::BUILD_IN_TRANSPORTS[$scheme])) {
            // Validate scheme contains only safe characters for class name resolution.
            if (!preg_match('/^[a-zA-Z][a-zA-Z0-9]*$/', $scheme)) {
                throw new RuntimeException("Invalid protocol scheme '$scheme'");
            }
            $scheme = ucfirst($scheme);
            $this->protocol = '\\Protocols\\' . $scheme;
            if (!class_exists($this->protocol)) {
                $this->protocol = "\\Workerman\\Protocols\\$scheme";
                if (!class_exists($this->protocol)) {
                    throw new RuntimeException("class \\Protocols\\$scheme not exist");
                }
            }
        } else {
            $this->transport = self::BUILD_IN_TRANSPORTS[$scheme];
        }

        // For statistics.
        ++self::$statistics['connection_count'];
        $this->maxSendBufferSize = self::$defaultMaxSendBufferSize;
        $this->maxPackageSize = self::$defaultMaxPackageSize;
        $this->socketContext = $socketContext;
        static::$connections[$this->realId] = $this;
        $this->context = new stdClass;
    }

    /**
     * Reconnect.
     *

View on GitHub (pinned to 1391112a61)

Solutions

  1. Create the protocol class, e.g. Protocols/Myproto.php with namespace Protocols and class Myproto implementing the protocol interface (input/output methods)
  2. Make sure composer.json autoloads the namespace: "autoload": {"psr-4": {"Protocols\\": "Protocols/"}} and run 'composer dump-autoload'
  3. If you meant a stock protocol, use its exact scheme: http://, ws://, text://, frame://

Example fix

// before
// config: 'myproto://127.0.0.1:9000' but no class exists
$conn = new AsyncTcpConnection('myproto://127.0.0.1:9000'); // throws class \Protocols\Myproto not exist

// after
// Protocols/Myproto.php
namespace Protocols;
class Myproto
{
    public static function input(string $buffer, $connection): int { /* ... */ return strlen($buffer); }
    public static function decode(string $buffer, $connection) { return $buffer; }
    public static function encode($data, $connection): string { return (string)$data; }
}
// composer.json: "autoload": {"psr-4": {"Protocols\\": "Protocols/"}} then: composer dump-autoload
Defensive patterns

Strategy: validation

Validate before calling

$scheme = 'myproto'; // from config
$builtin = ['tcp','udp','unix','ssl','sslv2','sslv3','tls'];
$known = in_array(strtolower($scheme), $builtin, true)
    || class_exists('\\Protocols\\' . ucfirst($scheme))
    || class_exists('\\Workerman\\Protocols\\' . ucfirst($scheme));
if (!$known) {
    throw new InvalidArgumentException("Protocol '$scheme' has no class; define Protocols\\" . ucfirst($scheme));
}
$conn = new AsyncTcpConnection("$scheme://host:port");

Try / catch

try { $conn = new AsyncTcpConnection($address); } catch (RuntimeException $e) { if (str_contains($e->getMessage(), 'not exist')) { /* log config error, skip endpoint */ } throw $e; }

Prevention

When it happens

Trigger: new AsyncTcpConnection('myproto://host:80') without a Protocols\Myproto class; typo in a known protocol name, e.g. 'frame2://' or 'etxt://' instead of 'text://'; protocol class exists but the 'Protocols' namespace is not registered in composer's PSR-4 autoload; class filename casing does not match the ucfirst()-derived class name.

Common situations: New project where the custom protocol file was not created yet or sits in the wrong directory; composer.json missing the {"Protocols\\": "Protocols/"} PSR-4 mapping; forgot to run 'composer dump-autoload' after adding the mapping; on case-sensitive filesystems the file must be named exactly like the resolved class (e.g. Myproto.php).

Related errors


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