nats-io/nats-server · error

character not supported for MQTT topics

Error message

character not supported for MQTT topics

What it means

errMQTTUnsupportedCharacters is returned when a topic (or subject) contains characters that are not allowed in MQTT topics. Specific bytes such as 0x7f (DEL, used internally as a SubjectTree pivot marker) and other control/unsupported characters are rejected to keep the subject tree consistent and to prevent control-line splitting when subjects are forwarded to other connection types (e.g. leaf nodes). Thrown from the topic-character validation switch around server/mqtt.go:6234-6240.

Source

Thrown at server/mqtt.go:243

	errMQTTNotWebsocketPort           = errors.New("MQTT clients over websocket must connect to the Websocket port, not the MQTT port")
	errMQTTTopicFilterCannotBeEmpty   = errors.New("topic filter cannot be empty")
	errMQTTMalformedVarInt            = errors.New("malformed variable int")
	errMQTTSecondConnectPacket        = errors.New("received a second CONNECT packet")
	errMQTTServerNameMustBeSet        = errors.New("mqtt requires server name to be explicitly set")
	errMQTTUserMixWithUsersNKeys      = errors.New("mqtt authentication username not compatible with presence of users/nkeys")
	errMQTTTokenMixWIthUsersNKeys     = errors.New("mqtt authentication token not compatible with presence of users/nkeys")
	errMQTTAckWaitMustBePositive      = errors.New("ack wait must be a positive value")
	errMQTTJSAPITimeoutMustBePositive = errors.New("JS API timeout must be a positive value")
	errMQTTStandaloneNeedsJetStream   = errors.New("mqtt requires JetStream to be enabled if running in standalone mode")
	errMQTTConnFlagReserved           = errors.New("connect flags reserved bit not set to 0")
	errMQTTWillAndRetainFlag          = errors.New("if Will flag is set to 0, Will Retain flag must be 0 too")
	errMQTTPasswordFlagAndNoUser      = errors.New("password flag set but username flag is not")
	errMQTTCIDEmptyNeedsCleanFlag     = errors.New("when client ID is empty, clean session flag must be set to 1")
	errMQTTEmptyWillTopic             = errors.New("empty Will topic not allowed")
	errMQTTEmptyUsername              = errors.New("empty user name not allowed")
	errMQTTTopicIsEmpty               = errors.New("topic cannot be empty")
	errMQTTPacketIdentifierIsZero     = errors.New("packet identifier cannot be 0")
	errMQTTUnsupportedCharacters      = errors.New("character not supported for MQTT topics")
	errMQTTInvalidSession             = errors.New("invalid MQTT session")
	errMQTTInvalidRetainFlags         = errors.New("invalid retained message flags")
	errMQTTInvalidRetainedMessage     = errors.New("invalid retained message")
	errMQTTSessionCollision           = errors.New("stored session does not match client ID")
	errMQTTInvalidPublishLength       = errors.New("invalid publish message, variable header exceeds remaining length")
	errMQTTAckPipelineStopped         = errors.New("QoS1 PUBACK pipeline has shut down while admitting a message, " +
		"abandoning the wait for its JetStream ack; failing the connection, " +
		"the client will re-send unacknowledged PUBLISH packets on reconnect")
)

type srvMQTT struct {
	listener     net.Listener
	listenerErr  error
	authOverride bool
	sessmgr      mqttSessionManager
}

type mqttSessionManager struct {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Sanitize topics before use: strip or replace DEL and control characters, and keep topics to allowed MQTT characters.
  2. Restrict topic names to [A-Za-z0-9] plus '/', '_', '-' style safe sets in your publish/subscribe call sites.
  3. Purge or re-publish retained messages stored with unsupported characters.

Example fix

// before
nc.Publish(userInput+"/status", data)
// after
topic := sanitizeTopic(userInput) // remove DEL/control chars, reject empty
nc.Publish(topic+"/status", data)
Defensive patterns

Strategy: validation

Validate before calling

func sanitizeTopic(topic string) (string, error) {
    for _, r := range topic {
        if r == 0x7f || (r < 0x20 && r != '/') {
            return "", fmt.Errorf("unsupported character %q in topic", r)
        }
    }
    return topic, nil
}

Type guard

func hasOnlySafeTopicChars(topic string) bool {
    for _, r := range topic {
        if r == 0x7f || (r < 0x20) {
            return false
        }
    }
    return len(topic) > 0
}

Try / catch

if _, err := sanitizeTopic(topic); err != nil {
    log.Warn("rejecting topic with unsupported characters", "topic", topic)
    return
}

Prevention

When it happens

Trigger: Publishing or subscribing to a topic containing DEL (0x7f), other control bytes, or characters forbidden for MQTT topics; retained-message recovery hits such a character and the validation returns the error.

Common situations: Applications interpolating raw binary payloads, filenames, or user input into topic names; topics built from strings containing '\x7f' or newline/control characters; legacy data in a retained-message stream containing unsupported bytes.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/2aba9118a5457328. Report an issue: GitHub.