{"record":{"id":"73c3d8ce84f49643","repo":"nats-io/nats-server","slug":"roll-ups-require-the-purge-permission","errorCode":null,"errorMessage":"roll-ups require the purge permission","messagePattern":"roll-ups require the purge permission","errorType":"error_code","errorClass":"JSStreamInvalidConfigError","httpStatus":null,"severity":"error","filePath":"server/stream.go","lineNumber":1990,"sourceCode":"\t\t}\n\t}\n\tif cfg.Duplicates < 0 {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"duplicates window can not be negative\"))\n\t}\n\t// Check that duplicates is not larger then age if set.\n\tif cfg.MaxAge != 0 && cfg.Duplicates > cfg.MaxAge {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"duplicates window can not be larger then max age\"))\n\t}\n\tif lim.Duplicates > 0 && cfg.Duplicates > lim.Duplicates {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"duplicates window can not be larger then server limit of %v\",\n\t\t\tlim.Duplicates.String()))\n\t}\n\tif cfg.Duplicates > 0 && cfg.Duplicates < 100*time.Millisecond {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"duplicates window needs to be >= 100ms\"))\n\t}\n\n\tif cfg.DenyPurge && cfg.AllowRollup {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"roll-ups require the purge permission\"))\n\t}\n\n\t// Counter is not compatible with some settings.\n\tif cfg.AllowMsgCounter {\n\t\tif cfg.Discard == DiscardNew {\n\t\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"counter stream cannot use discard new\"))\n\t\t}\n\t\tif cfg.AllowMsgTTL {\n\t\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"counter stream cannot use message TTLs\"))\n\t\t}\n\t\tif cfg.AllowMsgSchedules {\n\t\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"counter stream cannot use message schedules\"))\n\t\t}\n\t\tif cfg.Retention != LimitsPolicy {\n\t\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"counter stream can only use limits retention\"))\n\t\t}\n\t}\n","sourceCodeStart":1972,"sourceCodeEnd":2008,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/stream.go#L1972-L2008","documentation":"AllowRollup lets publishers replace or truncate stream contents via rollup headers, which destroys data, so the config forbids combining it with DenyPurge (which explicitly revokes destructive purge operations). Validation in server/stream.go returns a JSStreamInvalidConfigError when both flags are set.","triggerScenarios":"AddStream/UpdateStream with StreamConfig.AllowRollup=true and DenyPurge=true, typically when configuring permissions around rollups per subject.","commonSituations":"Operators hardening streams by denying purge while separately enabling rollups for snapshot-style publishes; copying permission flags from another stream without realizing the interaction.","solutions":["Set DenyPurge=false if rollups are required for the stream.","Or set AllowRollup=false if purge must remain denied.","Split into two streams: one with rollups enabled, one restricted without.","Control destructive access at the user/permission level instead of mixing these stream flags."],"exampleFix":"// before\n StreamConfig{Name: \"SNAP\", AllowRollup: true, DenyPurge: true}\n// after\n StreamConfig{Name: \"SNAP\", AllowRollup: true, DenyPurge: false}","handlingStrategy":"validation","validationCode":"if cfg.AllowRollup && cfg.DenyPurge {\n    return fmt.Errorf(\"AllowRollup conflicts with DenyPurge; pick one\")\n}","typeGuard":null,"tryCatchPattern":"var scErr *jetstream.JSApiError\nif _, err := js.UpdateStream(ctx, cfg); errors.As(err, &scErr) && strings.Contains(scErr.Description, \"roll-ups require the purge permission\") {\n    cfg.DenyPurge = false\n    _, err = js.UpdateStream(ctx, cfg)\n}","preventionTips":["When enabling rollups, explicitly set DenyPurge=false and note it in code review.","Copy permission flags between streams only after checking both flags.","Manage destructive-operation access at user permissions, not via these flags together."],"tags":["jetstream","stream-config","permissions","rollup","nats"],"backgroundTag":"invalid-stream-config","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}