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
- Ensure WsServer.php is stored and served as UTF-8: re-download from the official release or `composer install` fresh.
- 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.
- 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
- Deploy vendor files as-is (composer install on the server or download archives) — never re-encode .php sources.
- Verify editor/toolchain saves files as UTF-8 without BOM.
- Keep mbstring.func_overload at 0 on legacy PHP; confirm with `php -i`.
- Checksum vendor files after rsync/FTP transfers that might translate characters.
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
- Argument #4 ($loop) expected…
- $request can not be null
- All routes must implement Ratchet\Http\HttpServerInterface
- Invalid domain
- Invalid Port
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)