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
- Check JetStream health (server /jsz, stream state for $MQTT.sess)
- Retry the CONNECT once JetStream is available
- Verify stream retention/limits have not discarded needed records unexpectedly
- 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
- Monitor JetStream health (/jsz) and alert on degradation
- Ensure the $MQTT.sess stream exists with sane limits
- Keep cluster quorum healthy before rolling MQTT clients
- Read the wrapped cause (%w) in server logs to identify the root JetStream error
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
- unable to persist session %q (seq=%v): %v
- unable to delete session %q record at sequence %v: %v
- ack wait must be a positive value
- JS API timeout must be a positive value
- mqtt requires JetStream to be enabled if running in standalo
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/db9ea80a428e0e8f.
Report an issue: GitHub.