ratchetphp/Ratchet · error · DomainException

Bad encoding, unicode character ✓ did not match expected…

Error message

Bad encoding, unicode character ✓ did not match expected value. Ensure charset UTF-8 and check ini val mbstring.func_autoload

What it means

WsServer's constructor performs a runtime sanity check that the literal UTF-8 character '✓' encodes to the expected bytes (e29c93). If bin2hex output differs, the file or PHP runtime is not operating on UTF-8, which would corrupt WebSocket payloads, so a DomainException is thrown at startup.

Solutions

  1. Ensure WsServer.php is stored and served as UTF-8: re-download from the official release or `composer install` fresh.
  2. Check `php -i | grep mbstring.func_overload` and set mbstring.func_overload to 0 (remove the ini override) — note func_overload is removed in PHP 7.4+, so the real cause is usually file encoding.
  3. Fix the editor/toolchain that re-encodes the file (set files encoding to UTF-8 without BOM) and redeploy.

Example fix

; php.ini — before
mbstring.func_overload = 2
; after
mbstring.func_overload = 0
Defensive patterns

Strategy: validation

Validate before calling

if (bin2hex('✓') !== 'e29c93') {
    // detect encoding corruption before constructing WsServer
    error_log('Non-UTF-8 runtime/file encoding detected');
}
$ws = new \Ratchet\WebSocket\WsServer($app);

Try / catch

try {
    $ws = new \Ratchet\WebSocket\WsServer($app);
} catch (\DomainException $e) {
    // fatal environment problem: abort boot with a clear message
    exit('Server files must be UTF-8; check mbstring.func_overload and file encoding');
}

Prevention

When it happens

Trigger: Constructing WsServer while the source file has been re-encoded (e.g. saved as ISO-8859-1 or a BOM/encoding mangling changed the '✓' bytes), or PHP ini settings/extensions altering string encoding semantics (historic mbstring.func_overload misconfiguration).

Common situations: Deployment pipelines or editors re-encoding WsServer.php; rsync/FTP transfer with character translation; servers with mbstring.func_overload=2 enabled (legacy) affecting string functions.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of ratchetphp/Ratchet@e621c6c40b (2026-09-16). Data as JSON: /api/errors/e934a61a8828acde. Report an issue: GitHub.

Appendix: source

Thrown at src/Ratchet/WebSocket/WsServer.php:83

    /**
     * @param \Ratchet\WebSocket\MessageComponentInterface|\Ratchet\MessageComponentInterface $component Your application to run with WebSockets
     * @note If you want to enable sub-protocols have your component implement WsServerInterface as well
     */
    public function __construct(ComponentInterface $component) {
        if ($component instanceof MessageComponentInterface) {
            $this->msgCb = function(ConnectionInterface $conn, MessageInterface $msg) {
                $this->delegate->onMessage($conn, $msg);
            };
        } elseif ($component instanceof DataComponentInterface) {
            $this->msgCb = function(ConnectionInterface $conn, MessageInterface $msg) {
                $this->delegate->onMessage($conn, $msg->getPayload());
            };
        } else {
            throw new \UnexpectedValueException('Expected instance of \Ratchet\WebSocket\MessageComponentInterface or \Ratchet\MessageComponentInterface');
        }

        if (bin2hex('✓') !== 'e29c93') {
            throw new \DomainException('Bad encoding, unicode character ✓ did not match expected value. Ensure charset UTF-8 and check ini val mbstring.func_autoload');
        }

        $this->delegate    = $component;
        $this->connections = new \SplObjectStorage;

        $this->closeFrameChecker   = new CloseFrameChecker;

        if (self::isRFC6455v03()) {
            $this->handshakeNegotiator = new ServerNegotiator(new RequestVerifier);
        } else {
            $this->handshakeNegotiator = new ServerNegotiator(new RequestVerifier, new HttpFactory);
        }

        $this->handshakeNegotiator->setStrictSubProtocolCheck(true);

        if ($component instanceof WsServerInterface) {
            $this->handshakeNegotiator->setSupportedSubProtocols($component->getSubProtocols());
        }

View on GitHub (pinned to e621c6c40b)