nats-io/nats-server · error

mqtt: consumer_replicas (%v) cannot be higher than stream_re

Error message

mqtt: consumer_replicas (%v) cannot be higher than stream_replicas (%v)

What it means

MQTT option validation requires the MQTT consumer's replica count to not exceed its backing stream's replica count; the consumer cannot have more replicas than the stream it reads from. Config with consumer_replicas > stream_replicas (both > 0) is rejected at startup.

Source

Thrown at server/mqtt.go:742

	if mo.AckWait < 0 {
		return errMQTTAckWaitMustBePositive
	}
	if mo.JSAPITimeout < 0 {
		return errMQTTJSAPITimeoutMustBePositive
	}
	// If strictly standalone and there is no JS enabled, then it won't work...
	// For leafnodes, we could either have remote(s) and it would be ok, or no
	// remote but accept from a remote side that has "hub" property set, which
	// then would ok too. So we fail only if we have no leafnode config at all.
	if !o.JetStream && o.Cluster.Port == 0 && o.Gateway.Port == 0 &&
		o.LeafNode.Port == 0 && len(o.LeafNode.Remotes) == 0 {
		return errMQTTStandaloneNeedsJetStream
	}
	if err := validatePinnedCerts(mo.TLSPinnedCerts); err != nil {
		return fmt.Errorf("mqtt: %v", err)
	}
	if mo.ConsumerReplicas > 0 && mo.StreamReplicas > 0 && mo.ConsumerReplicas > mo.StreamReplicas {
		return fmt.Errorf("mqtt: consumer_replicas (%v) cannot be higher than stream_replicas (%v)",
			mo.ConsumerReplicas, mo.StreamReplicas)
	}
	return nil
}

// Returns true if this connection is from a MQTT client.
// Lock held on entry.
func (c *client) isMqtt() bool {
	return c.mqtt != nil
}

// If this is an MQTT client, returns the session client ID,
// otherwise returns the empty string.
// Lock held on entry
func (c *client) getMQTTClientID() string {
	if !c.isMqtt() {
		return _EMPTY_
	}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Raise stream_replicas to be >= consumer_replicas (e.g. both 3)
  2. Lower consumer_replicas to match stream_replicas
  3. Set one (or both) to 0 to use the server's defaults, which are always consistent

Example fix

// before (nats-server.conf)
mqtt {
  consumer_replicas: 3
  stream_replicas: 1
}
// after
mqtt {
  consumer_replicas: 3
  stream_replicas: 3
}
Defensive patterns

Strategy: validation

Validate before calling

func validMqttReplicas(cr, sr int) bool {
	return cr <= 0 || sr <= 0 || cr <= sr
}
// reject configs where consumer_replicas > stream_replicas before load

Try / catch

if err := srv.ValidateMqttOpts(); err != nil {
	if strings.Contains(err.Error(), "cannot be higher than") {
		return fmt.Errorf("fix mqtt replica settings: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: Setting mqtt { consumer_replicas: 3, stream_replicas: 1 } (or similar) in the server config; calling ValidateMqttOpts on Options where MqttConsumerReplicas > MqttStreamReplicas > 0.

Common situations: Tuning MQTT durability by raising consumer_replicas without also raising stream_replicas; copying HA settings from other JetStream configs where consumer/stream roles differ.

Related errors


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