nats-io/nats-server · error

loading session record: %w

Error message

loading session record: %w

What it means

Wraps a JetStream error that occurred while loading a persisted MQTT session record from the '$MQTT.sess' stream via loadSessionMsg. Only returned when the error is something OTHER than 'message not found' (not-found leads to session creation instead), so this signals real JetStream/storage trouble.

Source

Thrown at server/mqtt.go:3163

// Creates the session stream (limit msgs of 1) for this client ID if it does
// not already exist. If it exists, recover the single record to rebuild the
// state of the session. If there is a session record but this session is not
// registered in the runtime of this server, then a request is made to the
// owner to close the client associated with this session since specification
// [MQTT-3.1.4-2] specifies that if the ClientId represents a Client already
// connected to the Server then the Server MUST disconnect the existing client.
//
// Runs from the client's readLoop.
// Lock not held on entry, but session is in the locked map.
func (as *mqttAccountSessionManager) createOrRestoreSession(clientID string, opts *Options) (*mqttSession, bool, error) {
	jsa := &as.jsa

	hash := getHash(clientID)
	smsg, err := jsa.loadSessionMsg(as.domainTk, hash)
	if err != nil {
		if isErrorOtherThan(err, JSNoMessageFoundErr) {
			return nil, false, fmt.Errorf("loading session record: %w", err)
		}
		// Message not found, so reate the session...
		// Create a session and indicate that this session did not exist.
		sess := mqttSessionCreate(jsa, clientID, hash, 0, opts)
		sess.domainTk = as.domainTk
		return sess, false, nil
	}
	// We need to recover the existing record now.
	ps := &mqttPersistedSession{}
	if err := json.Unmarshal(smsg.Data, ps); err != nil {
		return nil, false, fmt.Errorf("unmarshal of session record at sequence %v: %w", smsg.Sequence, err)
	}
	if ps.ID != clientID {
		return nil, false, errMQTTSessionCollision
	}
	for sid, cc := range ps.Cons {
		if cc == nil {
			delete(ps.Cons, sid)

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Check JetStream health (server /jsz, stream state for $MQTT.sess)
  2. Retry the CONNECT once JetStream is available
  3. Verify stream retention/limits have not discarded needed records unexpectedly
  4. Inspect server logs for the wrapped underlying error (%w cause)

Example fix

// before
// CONNECT fails with opaque JetStream error
// after
// start/restart JetStream, ensure stream $MQTT.sess exists: nats stream info $MQTT.sess
null
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check JetStream availability before connecting
const js = await fetch('http://monitor:8222/jsz').then(r => r.json())
if (!js || !js.config) throw new Error('JetStream not available')

Try / catch

try {
  await mqttConnect()
} catch (e) {
  if (String(e).includes('loading session record')) {
    await waitForJetStreamHealthy()
    await mqttConnect()
  } else { throw e }
}

Prevention

When it happens

Trigger: jsa.loadSessionMsg fails with a non-JSNoMessageFoundErr error during session lookup — e.g. JetStream stream unavailable, timeout, API error, or storage failure when fetching the session message for the client ID hash.

Common situations: JetStream not running or degraded; '$MQTT.sess' stream limits misconfigured; cluster quorum lost; disk errors on the server storing the session.

Related errors


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