ratchetphp/Ratchet · error · Exception
Invalid Topic, must be a string
Error message
Invalid Topic, must be a string
What it means
Thrown when a WAMP message's element at index 1 — the Topic/URI used by PREFIX, CALL, SUBSCRIBE, UNSUBSCRIBE and PUBLISH frames — is neither a string nor a number. The check `isset($json[1]) && !(is_string($json[1]) || is_numeric($json[1]))` guards the topic before dispatch, because `getUri()` and prefix lookup require a string topic URI.
Solutions
- Ensure element 1 of every client-to-server WAMP frame (SUBSCRIBE/UNSUBSCRIBE/PUBLISH topic, CALL proc URI, PREFIX shortname) is a plain string, e.g. [5, "http://example.com/topic"].
- Fix client-side frame construction order: WAMP v1 expects [type, topic, ...payload], not [type, options, topic].
- Add a client-side serializer that validates the frame shape before sending: assert(typeof msg[1] === 'string').
- On the server, catch the exception in onError and log the connection to identify which client is sending malformed frames.
Example fix
// before (client)
ws.send(JSON.stringify([5, { uri: 'kittens' }]));
// after
ws.send(JSON.stringify([5, 'kittens'])); Defensive patterns
Strategy: validation
Validate before calling
$frame = json_decode($msg, true);
if (isset($frame[1]) && !is_string($frame[1]) && !is_numeric($frame[1])) {
// reject: element 1 (topic/proc URI) must be a string or number
} Type guard
function hasValidWampTopic(array $frame): bool {
return !isset($frame[1]) || is_string($frame[1]) || is_numeric($frame[1]);
} Try / catch
try {
$server->onMessage($conn, $msg);
} catch (\Ratchet\Wamp\Exception $e) {
error_log("Bad topic from client: " . $msg);
$conn->close();
} Prevention
- Construct frames as [type, topicString, ...] with the topic always a plain string.
- Never put WAMP v2-style option objects in position 1 of v1 frames.
- Validate topic shape client-side (typeof topic === 'string') before send.
- Log raw frames on server protocol errors to find the offending client quickly.
When it happens
Trigger: A client sends a frame like [5, {"uri": "topic"}] or [5, ["topic"]] or [5, null] where the second element is an array, object or null instead of a string topic URI. Note null at index 1 actually fails isset() and would not trigger; triggers specifically for arrays, booleans (after json_decode arrays) and nested structures.
Common situations: Client serialization bugs that nest the topic inside an object; WAMP v2-style publish options objects placed in the wrong position, e.g. [7, {exclude: ...}, topic] instead of legacy [7, topic]; hand-written protocol implementations misordering the frame elements.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid WAMP message format
- Invalid WAMP message type
- Maximum buffer size of
- $request can not be null
- Expected instance of…
AI-assisted analysis of ratchetphp/Ratchet@e621c6c40b (2026-09-16).
Data as JSON: /api/errors/324f360ae1a5ab3a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Ratchet/Wamp/ServerProtocol.php:97
/**
* {@inheritdoc}
* @throws \Ratchet\Wamp\Exception
* @throws \Ratchet\Wamp\JsonException
*/
public function onMessage(ConnectionInterface $from, $msg) {
$from = $this->connections[$from];
if (null === ($json = @json_decode($msg, true))) {
throw new JsonException;
}
if (!is_array($json) || $json !== array_values($json)) {
throw new Exception("Invalid WAMP message format");
}
if (isset($json[1]) && !(is_string($json[1]) || is_numeric($json[1]))) {
throw new Exception('Invalid Topic, must be a string');
}
switch ($json[0]) {
case static::MSG_PREFIX:
$from->WAMP->prefixes[$json[1]] = $json[2];
break;
case static::MSG_CALL:
array_shift($json);
$callID = array_shift($json);
$procURI = array_shift($json);
if (count($json) == 1 && is_array($json[0])) {
$json = $json[0];
}
$this->_decorating->onCall($from, $callID, $from->getUri($procURI), $json);
break;View on GitHub (pinned to e621c6c40b)