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

  1. Read the wrapped inner error to identify the underlying JetStream failure.
  2. Increase account JS stream/storage limits or free disk space.
  3. Verify cluster size supports the configured StreamReplicas.
  4. 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

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


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