nats-io/nats-server · error
create QoS2 outgoing PUBREL stream for account %q: %v
Error message
create QoS2 outgoing PUBREL stream for account %q: %v
What it means
MQTT outgoing PUBREL tracking (QoS2 handshake step 2) is persisted in a per-account stream on subjects mqttOutSubjectPrefix + '>' with FileStorage and InterestPolicy. If its creation fails with an error other than JSStreamNameExistErr, this wrapped error names the stream role, account, and underlying JS error, failing the MQTT connection setup.
Source
Thrown at server/mqtt.go:1484
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)
}
}
// This is the only case where we need "si" after lookup/create
needToTransfer := true
si, err := lookupStream(mqttRetainedMsgsStreamName, "retained messages")
switch {
case err != nil:
return nil, err
case si == nil:
// Create the stream for retained messages.
cfg := &StreamConfig{
Name: mqttRetainedMsgsStreamName,
Subjects: []string{mqttRetainedMsgsStreamSubject + ">"},
Storage: FileStorage,
Retention: LimitsPolicy,
Replicas: replicas,View on GitHub (pinned to 3a66a489d2)
Solutions
- Read the wrapped inner error to identify the underlying JetStream failure.
- Increase account JS stream/storage limits or free disk space.
- Verify cluster size supports the configured StreamReplicas.
- Check JetStream status via monitoring and confirm the account has JS enabled.
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: account must have JetStream with available streams and storage
if (!account.jetstream.enabled) throw new Error('enable JetStream for the account before MQTT'); Try / catch
try {
await mqttConnect();
} catch (e) {
if (/create QoS2 outgoing PUBREL stream/.test(e.message)) {
// likely JS limits or health; remediate and retry with backoff
raiseAccountLimitsOrRestoreJS();
return withBackoff(mqttConnect);
}
throw e;
} Prevention
- Enable JetStream for every account using MQTT
- Keep storage and stream-count headroom on the account tier
- Match StreamReplicas to actual cluster size
- Monitor the '<account>-mqtt-out' stream health in production
When it happens
Trigger: jsa.createStream(cfg) for the '<account>-mqtt-out' (PUBREL) stream fails with anything other than JSStreamNameExistErr — account JS limits, storage quota, replicas exceeding cluster size, or JS API/permission errors during mqttJSStreamConfig.
Common situations: Account JetStream quota exhausted; JS disabled or unhealthy; cluster too small for the configured replicas; storage backend (file) full or misconfigured.
Related errors
- create QoS2 incoming messages 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/25a03e511619747b.
Report an issue: GitHub.