nats-io/nats-server · error

unable to delete session %q record at sequence %v: %v

Error message

unable to delete session %q record at sequence %v: %v

What it means

Wraps an error from deleting the session's persisted record message in the '$MQTT.sess' stream by sequence. Sequence-not-found and msg-delete-failed-with-store-msg-not-found are deliberately ignored (benign in a cluster); anything else fails the cleanup.

Source

Thrown at server/mqtt.go:3499

	for _, dur := range durs {
		if _, err := sess.jsa.deleteConsumer(mqttStreamName, dur, noWait); isErrorOtherThan(err, JSConsumerNotFoundErr) {
			return fmt.Errorf("unable to delete consumer %q for session %q: %v", dur, sess.id, err)
		}
	}
	if pubRelDur != _EMPTY_ {
		_, err := sess.jsa.deleteConsumer(mqttOutStreamName, pubRelDur, noWait)
		if isErrorOtherThan(err, JSConsumerNotFoundErr) {
			return fmt.Errorf("unable to delete consumer %q for session %q: %v", pubRelDur, sess.id, err)
		}
	}

	if seq > 0 {
		err := sess.jsa.deleteMsg(mqttSessStreamName, seq, !noWait)
		// Ignore the various errors indicating that the message (or sequence)
		// is already deleted, can happen in a cluster.
		if isErrorOtherThan(err, JSSequenceNotFoundErrF) {
			if isErrorOtherThan(err, JSStreamMsgDeleteFailedF) || !strings.Contains(err.Error(), ErrStoreMsgNotFound.Error()) {
				return fmt.Errorf("unable to delete session %q record at sequence %v: %v", id, seq, err)
			}
		}
	}
	return nil
}

// This will update the session record for this client in the account's MQTT
// sessions stream if the session had any change in the subscriptions.
//
// Runs from the client's readLoop.
// Lock not held on entry, but session is in the locked map.
func (sess *mqttSession) update(filters []*mqttFilter, add bool) error {
	// Evaluate if we need to persist anything.
	var needUpdate bool
	for _, f := range filters {
		if add {
			if f.qos == mqttSubAckFailure {
				continue

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Check JetStream health and that '$MQTT.sess' message deletion is permitted
  2. Retry the CONNECT once the cluster is stable
  3. Upgrade servers to consistent versions
  4. Inspect the wrapped cause for the precise JetStream API error

Example fix

// before
// session replace fails deleting old record
// after
// restore JetStream quorum then reconnect; verify: nats stream info $MQTT.sess
null
Defensive patterns

Strategy: retry

Try / catch

try {
  await mqttConnect() // replaces previous session
} catch (e) {
  if (String(e).includes('unable to delete session')) {
    await waitForJetStreamHealthy()
    await mqttConnect()
  } else { throw e }
}

Prevention

When it happens

Trigger: sess.jsa.deleteMsg(mqttSessStreamName, seq, !noWait) returns an error that is neither JSSequenceNotFoundErrF nor JSStreamMsgDeleteFailedF combined with ErrStoreMsgNotFound — e.g. permissions, API error, timeout, or stream unavailable.

Common situations: JetStream unavailable during session replacement (new CONNECT taking over a session); cluster instability; stream permission or internal API errors; server version mismatch in cluster.

Related errors


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