nats-io/nats-server · error

could not look up the retained messages stream for account %

Error message

could not look up the retained messages stream for account %q

What it means

Similar to the nil guard earlier: after the retained-messages transfer step (transferRMS) ran, the server checks si before reading si.Config and found it nil, meaning the stream could not be resolved even after transfer/retry. This indicates the retained messages stream for the account is simply not available.

Source

Thrown at server/mqtt.go:1565

		// to load all retained messages.
		si, err = lookupStream(mqttRetainedMsgsStreamName, "retained messages")
		if err != nil {
			return err
		}
		needToTransfer = false
		return nil
	}

	// Attempt to transfer all "single subject" retained messages to new
	// subjects. It may fail, will log its own error; ignore it the first time
	// and proceed to updating MaxMsgsPer. Then we invoke transferRMS() again,
	// which will get another chance to resolve the error; if not we bail there.
	if err = transferRMS(); err != nil {
		return nil, err
	}
	// Guard before dereferencing si.Config below.
	if si == nil {
		return nil, fmt.Errorf("could not look up the retained messages stream for account %q", accName)
	}

	// Now, if the stream does not have MaxMsgsPer set to 1, and there are no
	// more messages on the single $MQTT.rmsgs subject, update the stream again.
	if si.Config.MaxMsgsPer != 1 {
		si.Config.MaxMsgsPer = 1
		// We will need an up-to-date si, so don't use local variable here.
		if si, err = jsa.updateStream(&si.Config); err != nil {
			return nil, fmt.Errorf("failed to update stream config: %w", err)
		}
	}

	// If we failed the first time, there is now at most one lingering message
	// in the old subject. Try again (it will be a NO-OP if succeeded the first
	// time).
	if err = transferRMS(); err != nil {
		return nil, err
	}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Verify the stream exists: nats stream ls and look for the retained messages stream; reconnect MQTT clients so the server recreates it
  2. If the stream was deleted, disconnect/reconnect the MQTT session to trigger recreation
  3. Check cluster/meta health and logs for lookup failures
  4. Ensure no ops scripts delete JetStream streams used internally by the MQTT subsystem
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the internal stream exists and is healthy:
js, _ := nc.JetStream()
si, err := js.StreamInfo("$MQTT.rmsgs")
if err == nil {
    for _, r := range si.State.Replicas { /* check peer lag/health */ }
}

Type guard

func streamAvailable(si *nats.StreamInfo) bool { return si != nil }

Try / catch

if strings.Contains(err.Error(), "could not look up the retained messages stream") {
    // stream deleted or lookup raced; reconnect to trigger recreation
    return retryReconnect(ctx)
}

Prevention

When it happens

Trigger: transferRMS returned success but the lookup inside it still failed to populate si, or the stream was deleted concurrently between creation/update and this check; the retained messages stream does not exist for the account.

Common situations: Someone deleted the $MQTT.rmsgs stream while MQTT clients were connected; cluster instability causing lookups to fail; half-completed upgrade/migration of the retained messages stream.

Related errors


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