nats-io/nats-server · error · JSStreamWrongLastSequenceConstantError

10164

10164

Error message

last sequence mismatch

What it means

Within a batched publish, an 'Nats-Expected-Last-Sequence' header was found on a message that is not the first message of the batch. The expected-last-sequence check is only meaningful for the first message in a batch (the batch as a whole commits after the previous sequence), so nats-server rejects the batch with JSStreamWrongLastSequenceConstantError (API error 10164).

Source

Thrown at server/jetstream_batching.go:755

		counter.total = &initial
		counter.sources = sources
		counter.ops++
		if diff.counter == nil {
			diff.counter = map[string]*msgCounterRunningTotal{subject: counter}
		} else {
			diff.counter[subject] = counter
		}
	}

	if len(hdr) > 0 {
		// Expected last sequence.
		if seq, exists := getExpectedLastSeq(hdr); exists && seq != mset.clseq-mset.clfs {
			mlseq := mset.clseq - mset.clfs
			err := fmt.Errorf("last sequence mismatch: %d vs %d", seq, mlseq)
			return hdr, msg, 0, NewJSStreamWrongLastSequenceError(mlseq), err
		} else if exists && len(diff.inflight) > 0 {
			// Only the first message in a batch can contain an expected last sequence.
			err := fmt.Errorf("last sequence mismatch")
			return hdr, msg, 0, NewJSStreamWrongLastSequenceConstantError(), err
		}

		// Expected last sequence per subject.
		if seq, exists := getExpectedLastSeqPerSubject(hdr); exists {
			// Allow override of the subject used for the check.
			seqSubj := subject
			if optSubj := getExpectedLastSeqPerSubjectForSubject(hdr); optSubj != _EMPTY_ {
				seqSubj = copyString(optSubj)
			}

			// The subject is already written to in this batch, we can't allow
			// expected checks since they would be incorrect.
			if _, ok := diff.inflight[seqSubj]; ok {
				err := errors.New("last sequence by subject mismatch")
				return hdr, msg, 0, NewJSStreamWrongLastSequenceConstantError(), err
			}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Set Nats-Expected-Last-Sequence only on the first message of the batch; remove it from subsequent messages
  2. Use Nats-Expected-Last-Sequence-Per-Subject for per-message expectations inside a batch instead of the global header
  3. If independent per-message guarantees are required, split into separate single publishes
  4. Update any header-cloning helper to strip expectation headers from non-leader batch messages

Example fix

// before
for i, m := range msgs {
    m.Header.Set("Nats-Expected-Last-Sequence", expected) // set on every message
}
// after
for i, m := range msgs {
    if i == 0 { m.Header.Set("Nats-Expected-Last-Sequence", expected) }
    m.Header.Del("Nats-Expected-Last-Sequence") // keep only on the batch leader
}
Defensive patterns

Strategy: validation

Validate before calling

for i, m := range msgs {
    if i > 0 {
        m.Header.Del("Nats-Expected-Last-Sequence") // only the batch leader may carry it
    }
}

Type guard

func onlyFirstMsgExpectation(msgs []*nats.Msg) []*nats.Msg {
    for i, m := range msgs {
        if i > 0 { m.Header.Del("Nats-Expected-Last-Sequence") }
    }
    return msgs
}

Try / catch

err := js.PublishBatch(ctx, msgs)
var ae *nats.APIError
if errors.As(err, &ae) && ae.ErrorCode() == 10164 {
    // fix batch: expected-last-sequence must be on the first message only
}

Prevention

When it happens

Trigger: Using atomic or fast batch publishing and setting Nats-Expected-Last-Sequence on a second or later message in the same batch; a batching helper that copies the header onto every message instead of only the first; reusing a prepared message template that already contains the header for all batch entries.

Common situations: Custom batch publish code that clones a header map for each message; middleware that injects idempotency/expectation headers per message; migrating single-message publish code to batch APIs without moving the expectation header to the batch leader.

Related errors


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