walkor/workerman · error · RuntimeException

class \Protocols\$scheme not exist

Error message

class \Protocols\$scheme not exist

What it means

For AsyncUdpConnection, any scheme other than 'udp' is resolved as an application-layer protocol class: '\Protocols\<Scheme>' first, then '\Workerman\Protocols\<Scheme>'. When neither class can be autoloaded the constructor throws. This mirrors the TCP connection behavior and is how custom UDP wire formats are plugged in.

Source

Thrown at src/Connection/AsyncUdpConnection.php:92

     * @param string $remoteAddress
     * @throws Throwable
     */
    public function __construct($remoteAddress, $contextOption = [])
    {
        // Get the application layer communication protocol and listening address.
        [$scheme, $address] = explode(':', $remoteAddress, 2);
        // Check application layer protocol class.
        if ($scheme !== 'udp') {
            // 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");
                }
            }
        }

        $this->remoteAddress = substr($address, 2);
        $this->contextOption = $contextOption;
    }

    /**
     * For udp package.
     *
     * @param resource $socket
     * @return void
     */
    public function baseRead($socket): void
    {
        $recvBuffer = stream_socket_recvfrom($socket, static::MAX_UDP_PACKAGE_SIZE, 0, $remoteAddress);
        if (false === $recvBuffer || empty($remoteAddress)) {

View on GitHub (pinned to 1391112a61)

Solutions

  1. Create Protocols/Mydns.php with namespace Protocols, class Mydns, implementing input/decode/encode static methods
  2. Register the namespace in composer.json ("psr-4": {"Protocols\\": "Protocols/"}) and run composer dump-autoload
  3. Or switch to plain 'udp://host:port' if no application protocol layer is needed

Example fix

// before
$conn = new AsyncUdpConnection('mydns://8.8.8.8:53'); // throws class \Protocols\Mydns not exist

// after
// Protocols/Mydns.php
class Mydns { /* protocol implementation */ }
// with PSR-4 mapping for Protocols\\ and composer dump-autoload run
$conn = new AsyncUdpConnection('mydns://8.8.8.8:53');
Defensive patterns

Strategy: validation

Validate before calling

$scheme = 'mydns';
if ($scheme !== 'udp'
    && !class_exists('\\Protocols\\' . ucfirst($scheme))
    && !class_exists('\\Workerman\\Protocols\\' . ucfirst($scheme))) {
    throw new InvalidArgumentException("Define Protocols\\" . ucfirst($scheme) . ' before using ' . "$scheme://");
}
$conn = new AsyncUdpConnection("$scheme://8.8.8.8:53");

Prevention

When it happens

Trigger: new AsyncUdpConnection('mydns://host:53') without defining Protocols\Mydns; typo such as 'dns2://' instead of the intended protocol name; protocol file present but the Protocols namespace is not in composer's PSR-4 autoload map; case mismatch between file name and the ucfirst()-derived class name on Linux.

Common situations: Prototyping a DNS-like or game UDP protocol and forgetting the protocol class; moving code into a project whose composer.json lacks the Protocols PSR-4 mapping; forgetting 'composer dump-autoload' after adding the mapping.

Related errors


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