{"record":{"id":"7a8b78b7013b8006","repo":"nats-io/nats-server","slug":"js-stream-rollup-failed","errorCode":"JS_STREAM_ROLLUP_FAILED","errorMessage":"rollup not permitted","messagePattern":"rollup not permitted","errorType":"error_code","errorClass":"JSStreamRollupFailedError","httpStatus":null,"severity":"error","filePath":"server/jetstream_batching.go","lineNumber":929,"sourceCode":"\t\t\t\t\tvar smv StoreMsg\n\t\t\t\t\tsm, _ := mset.store.LoadLastMsg(schedSubj, &smv)\n\t\t\t\t\tinvalid = sm != nil && len(sliceHeader(JSSchedulePattern, sm.hdr)) == 0\n\t\t\t\t}\n\t\t\t\tif invalid {\n\t\t\t\t\tapiErr := NewJSMessageSchedulesSchedulerInvalidError()\n\t\t\t\t\treturn hdr, msg, 0, apiErr, apiErr\n\t\t\t\t}\n\t\t\t}\n\t\t} else if !sourced && len(sliceHeader(JSScheduler, hdr)) > 0 {\n\t\t\t// Clients may only use Nats-Scheduler alongside Nats-Schedule-Next.\n\t\t\tapiErr := NewJSMessageSchedulesSchedulerInvalidError()\n\t\t\treturn hdr, msg, 0, apiErr, apiErr\n\t\t}\n\n\t\t// Check for any rollups.\n\t\tif rollup := getRollup(hdr); rollup != _EMPTY_ {\n\t\t\tif (!allowRollup || denyPurge) && !sourced {\n\t\t\t\terr := errors.New(\"rollup not permitted\")\n\t\t\t\treturn hdr, msg, 0, NewJSStreamRollupFailedError(err), err\n\t\t\t}\n\t\t\tswitch rollup {\n\t\t\tcase JSMsgRollupSubject:\n\t\t\t\t// Rolling up the subject is only allowed if the first occurrence of this subject in the batch.\n\t\t\t\tif _, ok := diff.inflight[subject]; ok {\n\t\t\t\t\terr := errors.New(\"batch rollup sub invalid\")\n\t\t\t\t\treturn hdr, msg, 0, NewJSStreamRollupFailedError(err), err\n\t\t\t\t}\n\t\t\tcase JSMsgRollupAll:\n\t\t\t\t// Rolling up the whole stream is only allowed if this is the first message of the batch.\n\t\t\t\tif len(diff.inflight) > 0 {\n\t\t\t\t\terr := errors.New(\"batch rollup all invalid\")\n\t\t\t\t\treturn hdr, msg, 0, NewJSStreamRollupFailedError(err), err\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\terr := fmt.Errorf(\"rollup value invalid: %q\", rollup)\n\t\t\t\treturn hdr, msg, 0, NewJSStreamRollupFailedError(err), err","sourceCodeStart":911,"sourceCodeEnd":947,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/jetstream_batching.go#L911-L947","documentation":"checkMsgHeadersPreClusteredProposal validates rollup headers on batched publishes. A rollup (Nats-Rollup: sub|all) is only allowed when the stream permits rollups (allowRollup, purge not denied, non-sourced msgs) and the subject hasn't already been rolled up in this batch; otherwise JS_STREAM_ROLLUP_FAILED 'rollup not permitted' is returned.","triggerScenarios":"Publishing a rollup message to a stream with AllowRollup=false or DenyPurge=true; a second rollup for the same subject within one batch (diff.inflight collision).","commonSituations":"Streams created before rollup support or explicitly configured AllowRollup:false receiving rollup headers; compaction/aggregation jobs sending repeated rollups per subject in one batch; mirrored/restored streams with different policies.","solutions":["Enable rollups on the stream: `nats stream update ORDERS --allow-rollup` or set AllowRollup: true in StreamConfig.","Remove the Nats-Rollup header from messages published to streams that disallow rollups.","Send at most one rollup per subject per batch; split further rollups into later batches.","Check DenyPurge on the stream — rollups are denied when purging is denied unless the message is sourced."],"exampleFix":"// before: stream without rollup permission\nstreamCfg := &nats.StreamConfig{Name: \"ORDERS\"} // AllowRollup defaults false\njs.PublishMsg(&nats.Msg{Subject: \"orders.new\", Header: nats.Header{\"Nats-Rollup\": []string{\"sub\"}}, Data: data})\n// -> JS_STREAM_ROLLUP_FAILED rollup not permitted\n// after\nstreamCfg.AllowRollup = true\njs.UpdateStream(streamCfg)\njs.PublishMsg(&nats.Msg{Subject: \"orders.new\", Header: nats.Header{\"Nats-Rollup\": []string{\"sub\"}}, Data: data})","handlingStrategy":"validation","validationCode":"si, err := js.StreamInfo(\"ORDERS\")\nif err == nil && !si.Config.AllowRollup {\n\treturn fmt.Errorf(\"stream ORDERS disallows rollup; enable AllowRollup first\")\n}","typeGuard":null,"tryCatchPattern":"_, err := js.PublishMsg(&nats.Msg{Subject: subj, Header: rollupHdr, Data: data})\nvar apiErr *nats.APIError\nif errors.As(err, &apiErr) && apiErr.ErrorCode == nats.JSStreamRollupFailed {\n\t// enable rollup on the stream or republish without the header\n}","preventionTips":["Enable AllowRollup on streams that receive rollup headers; check DenyPurge too.","Send at most one rollup per subject per batch.","Document which streams accept rollups and gate publishers accordingly.","Re-verify rollup policies after stream recreation or mirroring."],"tags":["jetstream","rollup","stream-config","permissions"],"backgroundTag":"rollup-not-permitted","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}