{"record":{"id":"bcc9bfcd454a0fce","repo":"nats-io/nats-server","slug":"topic-cannot-be-empty","errorCode":null,"errorMessage":"topic cannot be empty","messagePattern":"topic cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mqtt.go","lineNumber":241,"sourceCode":"\nvar (\n\terrMQTTNotWebsocketPort           = errors.New(\"MQTT clients over websocket must connect to the Websocket port, not the MQTT port\")\n\terrMQTTTopicFilterCannotBeEmpty   = errors.New(\"topic filter cannot be empty\")\n\terrMQTTMalformedVarInt            = errors.New(\"malformed variable int\")\n\terrMQTTSecondConnectPacket        = errors.New(\"received a second CONNECT packet\")\n\terrMQTTServerNameMustBeSet        = errors.New(\"mqtt requires server name to be explicitly set\")\n\terrMQTTUserMixWithUsersNKeys      = errors.New(\"mqtt authentication username not compatible with presence of users/nkeys\")\n\terrMQTTTokenMixWIthUsersNKeys     = errors.New(\"mqtt authentication token not compatible with presence of users/nkeys\")\n\terrMQTTAckWaitMustBePositive      = errors.New(\"ack wait must be a positive value\")\n\terrMQTTJSAPITimeoutMustBePositive = errors.New(\"JS API timeout must be a positive value\")\n\terrMQTTStandaloneNeedsJetStream   = errors.New(\"mqtt requires JetStream to be enabled if running in standalone mode\")\n\terrMQTTConnFlagReserved           = errors.New(\"connect flags reserved bit not set to 0\")\n\terrMQTTWillAndRetainFlag          = errors.New(\"if Will flag is set to 0, Will Retain flag must be 0 too\")\n\terrMQTTPasswordFlagAndNoUser      = errors.New(\"password flag set but username flag is not\")\n\terrMQTTCIDEmptyNeedsCleanFlag     = errors.New(\"when client ID is empty, clean session flag must be set to 1\")\n\terrMQTTEmptyWillTopic             = errors.New(\"empty Will topic not allowed\")\n\terrMQTTEmptyUsername              = errors.New(\"empty user name not allowed\")\n\terrMQTTTopicIsEmpty               = errors.New(\"topic cannot be empty\")\n\terrMQTTPacketIdentifierIsZero     = errors.New(\"packet identifier cannot be 0\")\n\terrMQTTUnsupportedCharacters      = errors.New(\"character not supported for MQTT topics\")\n\terrMQTTInvalidSession             = errors.New(\"invalid MQTT session\")\n\terrMQTTInvalidRetainFlags         = errors.New(\"invalid retained message flags\")\n\terrMQTTInvalidRetainedMessage     = errors.New(\"invalid retained message\")\n\terrMQTTSessionCollision           = errors.New(\"stored session does not match client ID\")\n\terrMQTTInvalidPublishLength       = errors.New(\"invalid publish message, variable header exceeds remaining length\")\n\terrMQTTAckPipelineStopped         = errors.New(\"QoS1 PUBACK pipeline has shut down while admitting a message, \" +\n\t\t\"abandoning the wait for its JetStream ack; failing the connection, \" +\n\t\t\"the client will re-send unacknowledged PUBLISH packets on reconnect\")\n)\n\ntype srvMQTT struct {\n\tlistener     net.Listener\n\tlistenerErr  error\n\tauthOverride bool\n\tsessmgr      mqttSessionManager\n}","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/mqtt.go#L223-L259","documentation":"errMQTTTopicIsEmpty is returned when an MQTT PUBLISH packet is parsed and its topic name has zero length. The MQTT spec requires every PUBLISH to carry a non-empty topic string, so the server rejects the packet at parse time instead of forwarding a message with no destination. It is thrown from mqttParsePub (server/mqtt.go:4264) when len(pp.topic)==0 after decoding the variable header.","triggerScenarios":"A client sends a PUBLISH packet whose variable header encodes a topic of length 0 (two length bytes 0x00 0x00 and no topic bytes); mqttParsePub then returns errMQTTTopicIsEmpty and the packet is rejected.","commonSituations":"Hand-rolled or buggy MQTT client serialization that forgets to write the topic field; fuzzing or malformed-packet tests against the server; a truncated/corrupted PUBLISH where the topic bytes were dropped; protocol-level test vectors such as the {\"empty topic\", []byte{0,0}, 2, ...} case in mqtt_test.go.","solutions":["Fix the client so it writes a valid, non-empty topic (length-prefixed) into the PUBLISH variable header before sending.","Validate the topic string on the publishing side before constructing the packet (e.g. reject empty strings early).","If you are generating test/protocol bytes, include the topic length prefix plus topic bytes, e.g. []byte{0, 3, 'a', '/', 'b'}."],"exampleFix":"// before: client sends PUBLISH with empty topic\nbuf := []byte{0x30, remLen, 0x00, 0x00 /* zero-length topic */, payload...}\n// after: write a real topic\nbuf = appendVarInt(buf, len(topic))\nbuf = append(buf, topic...)","handlingStrategy":"validation","validationCode":"func validatePublishTopic(topic string) error {\n    if len(topic) == 0 {\n        return errors.New(\"topic cannot be empty\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := client.Publish(topic, payload); err != nil {\n    if strings.Contains(err.Error(), \"topic cannot be empty\") {\n        log.Warn(\"skipping publish: empty topic\")\n        return\n    }\n    return err\n}","preventionTips":["Always assign a non-empty topic constant or configuration value before publishing","Validate topic strings at the application boundary before building packets","Add a unit test asserting empty topics are rejected client-side"],"tags":["mqtt","protocol-validation","publish-packet"],"backgroundTag":"empty-topic-name","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}