nats-io/nats-server · error
create QoS2 incoming messages stream for account %q: %v
Error message
create QoS2 incoming messages stream for account %q: %v
What it means
For MQTT QoS2, each in-flight message gets its own subject in the QoS2 incoming-messages stream, configured with DiscardNew, MaxMsgsPer=1 and DiscardNewPer. If creating this stream fails for reasons other than it already existing (JSStreamNameExistErr), the error is wrapped with the account name and returned, aborting the MQTT session setup.
Source
Thrown at server/mqtt.go:1466
if si, err := lookupStream(mqttQoS2IncomingMsgsStreamName, "QoS2 incoming messages"); err != nil {
return nil, err
} else if si == nil {
// Create the stream for the incoming QoS2 messages that have not been
// PUBREL-ed by the sender. Subject is
// "$MQTT.qos2.<session>.<PI>", the .PI is to achieve exactly
// once for each PI.
cfg := &StreamConfig{
Name: mqttQoS2IncomingMsgsStreamName,
Subjects: []string{mqttQoS2IncomingMsgsStreamSubjectPrefix + ">"},
Storage: FileStorage,
Retention: LimitsPolicy,
Discard: DiscardNew,
MaxMsgsPer: 1,
DiscardNewPer: true,
Replicas: replicas,
}
if _, _, err := jsa.createStream(cfg); isErrorOtherThan(err, JSStreamNameExistErr) {
return nil, fmt.Errorf("create QoS2 incoming messages stream for account %q: %v", accName, err)
}
}
if si, err := lookupStream(mqttOutStreamName, "QoS2 outgoing PUBREL"); err != nil {
return nil, err
} else if si == nil {
// Create the stream for the incoming QoS2 messages that have not been
// PUBREL-ed by the sender. NATS messages are submitted as
// "$MQTT.pubrel.<session hash>"
cfg := &StreamConfig{
Name: mqttOutStreamName,
Subjects: []string{mqttOutSubjectPrefix + ">"},
Storage: FileStorage,
Retention: InterestPolicy,
Replicas: replicas,
}
if _, _, err := jsa.createStream(cfg); isErrorOtherThan(err, JSStreamNameExistErr) {
return nil, fmt.Errorf("create QoS2 outgoing PUBREL stream for account %q: %v", accName, err)View on GitHub (pinned to 3a66a489d2)
Solutions
- Check the wrapped inner error for the exact JS cause.
- Ensure the NATS server/JS version supports DiscardNewPer (upgrade if running an older JS tier).
- Raise the account's stream count/storage limits.
- Confirm JetStream health and permissions for the account.
Defensive patterns
Strategy: retry
Validate before calling
// Verify server/JS version supports DiscardNewPer before using QoS2 MQTT
if (serverInfo.proto < MIN_PROTO_DISCARD_NEW_PER) {
throw new Error('upgrade NATS server: QoS2 stream config requires newer JetStream');
} Try / catch
try {
await mqttConnect();
} catch (e) {
if (/create QoS2 incoming messages stream/.test(e.message)) {
if (/discard|config/.test(e.message)) upgradeServer();
else raiseAccountLimits();
return mqttConnect();
}
throw e;
} Prevention
- Run a NATS server version supporting per-subject discard limits
- Keep the account under max-stream quotas
- Watch for JS API errors when enabling QoS2 workloads
- Test QoS2 flows against your production JS tier before rollout
When it happens
Trigger: jsa.createStream(cfg) for the '<account>-mqtt-q2' stream returns an error other than JSStreamNameExistErr — e.g. account JS limits reject the per-subject discard configuration, storage limits hit, or JS API/permissions errors.
Common situations: Older/limited JS tiers that don't support DiscardNewPer; account max-streams quota reached; storage exhausted; JS temporarily unavailable.
Related errors
- create QoS2 outgoing PUBREL stream for account %q: %v
- create sessions stream for account %q: %v
- create messages stream for account %q: %v
- create retained messages stream for account %q: %v
- error creating store for stream
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/443048c6cb7c33f3.
Report an issue: GitHub.