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 - lseqView on GitHub (pinned to 3a66a489d2)
Solutions
- Check JetStream storage health: free disk space, filesystem errors, and server logs
- Delete the partially restored stream and restore into a fresh stream
- Retry after fixing the underlying storage issue reported in the server logs
- 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
- Ensure ample free disk space before restoring TTL-enabled streams
- Delete partial restore artifacts before retrying
- Monitor storage I/O errors in server logs
- Restore into a fresh stream for clean sequence reservation
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
- failed to process gap: %w
- failed to parse message TTL: %w
- failed to store message sequence %d: %w
- failed to process trailing gap: %w
- storage type can not be updated
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/8b1dceed9cc285e4.
Report an issue: GitHub.