{"record":{"id":"ac5a5d94a7787c3d","repo":"nats-io/nats-server","slug":"10164","errorCode":"10164","errorMessage":"last sequence mismatch","messagePattern":"last sequence mismatch","errorType":"error_code","errorClass":"JSStreamWrongLastSequenceConstantError","httpStatus":400,"severity":"error","filePath":"server/jetstream_batching.go","lineNumber":755,"sourceCode":"\t\tcounter.total = &initial\n\t\tcounter.sources = sources\n\t\tcounter.ops++\n\t\tif diff.counter == nil {\n\t\t\tdiff.counter = map[string]*msgCounterRunningTotal{subject: counter}\n\t\t} else {\n\t\t\tdiff.counter[subject] = counter\n\t\t}\n\t}\n\n\tif len(hdr) > 0 {\n\t\t// Expected last sequence.\n\t\tif seq, exists := getExpectedLastSeq(hdr); exists && seq != mset.clseq-mset.clfs {\n\t\t\tmlseq := mset.clseq - mset.clfs\n\t\t\terr := fmt.Errorf(\"last sequence mismatch: %d vs %d\", seq, mlseq)\n\t\t\treturn hdr, msg, 0, NewJSStreamWrongLastSequenceError(mlseq), err\n\t\t} else if exists && len(diff.inflight) > 0 {\n\t\t\t// Only the first message in a batch can contain an expected last sequence.\n\t\t\terr := fmt.Errorf(\"last sequence mismatch\")\n\t\t\treturn hdr, msg, 0, NewJSStreamWrongLastSequenceConstantError(), err\n\t\t}\n\n\t\t// Expected last sequence per subject.\n\t\tif seq, exists := getExpectedLastSeqPerSubject(hdr); exists {\n\t\t\t// Allow override of the subject used for the check.\n\t\t\tseqSubj := subject\n\t\t\tif optSubj := getExpectedLastSeqPerSubjectForSubject(hdr); optSubj != _EMPTY_ {\n\t\t\t\tseqSubj = copyString(optSubj)\n\t\t\t}\n\n\t\t\t// The subject is already written to in this batch, we can't allow\n\t\t\t// expected checks since they would be incorrect.\n\t\t\tif _, ok := diff.inflight[seqSubj]; ok {\n\t\t\t\terr := errors.New(\"last sequence by subject mismatch\")\n\t\t\t\treturn hdr, msg, 0, NewJSStreamWrongLastSequenceConstantError(), err\n\t\t\t}\n","sourceCodeStart":737,"sourceCodeEnd":773,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/jetstream_batching.go#L737-L773","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set Nats-Expected-Last-Sequence only on the first message of the batch; remove it from subsequent messages","Use Nats-Expected-Last-Sequence-Per-Subject for per-message expectations inside a batch instead of the global header","If independent per-message guarantees are required, split into separate single publishes","Update any header-cloning helper to strip expectation headers from non-leader batch messages"],"exampleFix":"// before\nfor i, m := range msgs {\n    m.Header.Set(\"Nats-Expected-Last-Sequence\", expected) // set on every message\n}\n// after\nfor i, m := range msgs {\n    if i == 0 { m.Header.Set(\"Nats-Expected-Last-Sequence\", expected) }\n    m.Header.Del(\"Nats-Expected-Last-Sequence\") // keep only on the batch leader\n}","handlingStrategy":"validation","validationCode":"for i, m := range msgs {\n    if i > 0 {\n        m.Header.Del(\"Nats-Expected-Last-Sequence\") // only the batch leader may carry it\n    }\n}\n","typeGuard":"func onlyFirstMsgExpectation(msgs []*nats.Msg) []*nats.Msg {\n    for i, m := range msgs {\n        if i > 0 { m.Header.Del(\"Nats-Expected-Last-Sequence\") }\n    }\n    return msgs\n}\n","tryCatchPattern":"err := js.PublishBatch(ctx, msgs)\nvar ae *nats.APIError\nif errors.As(err, &ae) && ae.ErrorCode() == 10164 {\n    // fix batch: expected-last-sequence must be on the first message only\n}\n","preventionTips":["Strip expectation headers from all but the first message when building batches","Use per-subject expectation headers for per-message checks inside batches","Centralize batch construction in one helper so header rules are enforced in one place"],"tags":["jetstream","batching","header-misuse","expected-last-sequence"],"backgroundTag":"jetstream-last-sequence-mismatch","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}