nats-io/nats-server · error

failed to process expired message sequence %d: %w

Error message

failed to process expired message sequence %d: %w

What it means

When a backed-up message has a positive TTL and its timestamp plus TTL is already in the past, RestoreStreamV2 skips storing it but must preserve the sequence by calling store.SkipMsgs(seq, 1). This error wraps a failure of that skip, meaning the destination store could not reserve the expired message's sequence.

Source

Thrown at server/stream_backup.go:535

		}
		// We could have deleted messages since the last message we stored, if so
		// we should work out what the gap is and skip those sequences.
		if gap := seq - lseq - 1; gap > 0 {
			if err := store.SkipMsgs(lseq+1, gap); err != nil {
				return nil, fmt.Errorf("failed to process gap: %w", err)
			}
		}
		lseq = seq
		ttl, err := getMessageTTL(mhdr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse message TTL: %w", err)
		}
		hdrTime := time.Unix(0, hdr.Timestamp)
		if ttl > 0 && time.Now().After(hdrTime.Add(time.Duration(ttl)*time.Second)) {
			// If the TTL has exceeded then there isn't much point in storing the message,
			// but we still need to preserve the sequence.
			if err := store.SkipMsgs(seq, 1); err != nil {
				return nil, fmt.Errorf("failed to process expired message sequence %d: %w", seq, err)
			}
			releaseRestoreBytes(storedSize)
			continue
		}
		if err = store.StoreRawMsg(subj, mhdr, msg, seq, hdr.Timestamp, ttl, false); err != nil {
			return nil, fmt.Errorf("failed to store message sequence %d: %w", seq, err)
		}
		releaseRestoreBytes(storedSize)
	}

	if !eob {
		return mset, fmt.Errorf("backup was truncated")
	}

	// Need to make sure that we pad out with skip msgs to preserve the last
	// sequence, otherwise trailing deleted messages could reuse sequence numbers.
	if lseq < nstate.LastSeq {
		gap := nstate.LastSeq - lseq

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Check JetStream storage health: free disk space, filesystem errors, and server logs
  2. Delete the partially restored stream and restore into a fresh stream
  3. Retry after fixing the underlying storage issue reported in the server logs
  4. Validate the destination stream's message blocks are consistent before re-attempting
Defensive patterns

Strategy: try-catch

Validate before calling

if free, err := diskFree(jsStoreDir); err != nil || free < neededBytes { return fmt.Errorf("insufficient storage for restore") }

Try / catch

if err != nil && strings.Contains(err.Error(), "expired message sequence") {
    // storage failure while skipping expired msgs; check server logs, fix storage, retry
}

Prevention

When it happens

Trigger: store.SkipMsgs(seq, 1) errors while skipping an already-expired message during restore; driven by storage-layer failures (disk full, I/O error, corrupted message block in the destination stream).

Common situations: Restoring backups containing expired per-message-TTL messages onto a server with failing storage or a destination stream in a bad state from a previous failed restore.

Related errors


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