{"record":{"id":"503f49fe39cf5793","repo":"nats-io/nats-server","slug":"unable-to-persist-session-q-seq-v-v","errorCode":null,"errorMessage":"unable to persist session %q (seq=%v): %v","messagePattern":"unable to persist session %q \\(seq=(.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mqtt.go","lineNumber":3435,"sourceCode":"\tsess.mu.Unlock()\n\n\tvar hdr int\n\tif seq != 0 {\n\t\tbb := bytes.Buffer{}\n\t\tbb.WriteString(hdrLine)\n\t\tbb.WriteString(JSExpectedLastSubjSeq)\n\t\tbb.WriteString(\":\")\n\t\tbb.WriteString(strconv.FormatInt(int64(seq), 10))\n\t\tbb.WriteString(CR_LF)\n\t\tbb.WriteString(CR_LF)\n\t\thdr = bb.Len()\n\t\tbb.Write(b)\n\t\tb = bb.Bytes()\n\t}\n\n\tresp, err := sess.jsa.storeSessionMsg(domainTk, cidHash, hdr, b)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to persist session %q (seq=%v): %v\", ps.ID, seq, err)\n\t}\n\t// Guard before dereferencing below.\n\tif resp == nil || resp.PubAck == nil {\n\t\treturn fmt.Errorf(\"unable to persist session %q (seq=%v): invalid pub ack response\", ps.ID, seq)\n\t}\n\tsess.mu.Lock()\n\tsess.seq = resp.Sequence\n\tsess.mu.Unlock()\n\treturn nil\n}\n\n// Clear the session.\n//\n// Runs from the client's readLoop.\n// Lock not held on entry, but session is in the locked map.\nfunc (sess *mqttSession) clear(noWait bool) error {\n\tvar durs []string\n\tvar pubRelDur string","sourceCodeStart":3417,"sourceCodeEnd":3453,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/mqtt.go#L3417-L3453","documentation":"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.","triggerScenarios":"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.","commonSituations":"JetStream degraded or restarted; cluster without quorum for the $MQTT.sess stream; MQTT session with an enormous state exceeding max_payload; disk full.","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"],"exampleFix":"// before\n// store fails: payload too large\n// after\n// raise max_payload or reduce per-session state (clean_session=true, fewer subs)\nnull","handlingStrategy":"retry","validationCode":"// Pre-check stream state and payload budget before large sessions\nconst info = await fetch('http://monitor:8222/jsz?streams=true').then(r=>r.json())\nconst sess = info.account_details?.flatMap(a=>a.streams||[]).find(s=>s.name==='$MQTT.sess')\nif (!sess || sess.state.messages < 0) throw new Error('$MQTT.sess stream unhealthy')","typeGuard":null,"tryCatchPattern":"try {\n  await mqttConnect()\n} catch (e) {\n  if (String(e).includes('unable to persist session')) {\n    await waitForJetStreamHealthy()\n    await mqttConnect({ cleanSession: true }) // fallback to avoid heavy persisted state\n  } else { throw e }\n}","preventionTips":["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"],"tags":["mqtt","jetstream","persistence"],"backgroundTag":"jetstream-publish-failed","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}