nats-io/nats-server · error
unable to persist session %q (seq=%v): %v
Error message
unable to persist session %q (seq=%v): %v
What it means
Returned when storing the persisted session record fails: sess.jsa.storeSessionMsg publishes the session state to the '$MQTT.sess' stream and the returned error is wrapped with the session ID, sequence, and cause. The session state could not be durably saved.
Source
Thrown at server/mqtt.go:3435
sess.mu.Unlock()
var hdr int
if seq != 0 {
bb := bytes.Buffer{}
bb.WriteString(hdrLine)
bb.WriteString(JSExpectedLastSubjSeq)
bb.WriteString(":")
bb.WriteString(strconv.FormatInt(int64(seq), 10))
bb.WriteString(CR_LF)
bb.WriteString(CR_LF)
hdr = bb.Len()
bb.Write(b)
b = bb.Bytes()
}
resp, err := sess.jsa.storeSessionMsg(domainTk, cidHash, hdr, b)
if err != nil {
return fmt.Errorf("unable to persist session %q (seq=%v): %v", ps.ID, seq, err)
}
// Guard before dereferencing below.
if resp == nil || resp.PubAck == nil {
return fmt.Errorf("unable to persist session %q (seq=%v): invalid pub ack response", ps.ID, seq)
}
sess.mu.Lock()
sess.seq = resp.Sequence
sess.mu.Unlock()
return nil
}
// Clear the session.
//
// Runs from the client's readLoop.
// Lock not held on entry, but session is in the locked map.
func (sess *mqttSession) clear(noWait bool) error {
var durs []string
var pubRelDur stringView on GitHub (pinned to 3a66a489d2)
Solutions
- Check JetStream/stream health for $MQTT.sess and cluster quorum
- Reduce session state size (fewer subscriptions / queued messages) if hitting max_payload
- Free disk space or fix filestore issues
- Retry the CONNECT/publish after the underlying JetStream error is resolved
Example fix
// before // store fails: payload too large // after // raise max_payload or reduce per-session state (clean_session=true, fewer subs) null
Defensive patterns
Strategy: retry
Validate before calling
// Pre-check stream state and payload budget before large sessions
const info = await fetch('http://monitor:8222/jsz?streams=true').then(r=>r.json())
const sess = info.account_details?.flatMap(a=>a.streams||[]).find(s=>s.name==='$MQTT.sess')
if (!sess || sess.state.messages < 0) throw new Error('$MQTT.sess stream unhealthy') Try / catch
try {
await mqttConnect()
} catch (e) {
if (String(e).includes('unable to persist session')) {
await waitForJetStreamHealthy()
await mqttConnect({ cleanSession: true }) // fallback to avoid heavy persisted state
} else { throw e }
} Prevention
- Keep session state small (subscriptions, queued messages) under max_payload
- Monitor $MQTT.sess stream health and disk capacity
- Ensure cluster quorum before reconnecting clients
- Use clean_session for clients that do not need durable sessions
When it happens
Trigger: storeSessionMsg returns a JetStream error — stream not available, publish timeout, no responders, maximum payload exceeded (many subscriptions/queued messages making the record too large), or storage failure.
Common situations: JetStream degraded or restarted; cluster without quorum for the $MQTT.sess stream; MQTT session with an enormous state exceeding max_payload; disk full.
Related errors
- loading session record: %w
- 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/503f49fe39cf5793.
Report an issue: GitHub.