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
- Raise stream_replicas to be >= consumer_replicas (e.g. both 3)
- Lower consumer_replicas to match stream_replicas
- 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
- Treat stream_replicas as the ceiling; derive consumer_replicas from it
- Set both replicas together when editing mqtt HA settings
- Validate MQTT config in CI by parsing options before deployment
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
- lookup %s stream for account %q: %v
- system account not setup
- JetStream cluster requires cluster name
- JetStream cluster requires configured routes or solicited le
- system limit reached
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/5843d0fbb2ad4f89.
Report an issue: GitHub.