{"record":{"id":"6abb2822d13e66e2","repo":"nats-io/nats-server","slug":"start-sequence-can-not-be-updated","errorCode":null,"errorMessage":"start sequence can not be updated","messagePattern":"start sequence can not be updated","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/consumer.go","lineNumber":2549,"sourceCode":"\t\treturn nil\n\t}\n\t// Something different, so check since we only allow certain things to be updated.\n\tif cfg.DeliverPolicy != ncfg.DeliverPolicy {\n\t\treturn errors.New(\"deliver policy can not be updated\")\n\t}\n\tif cfg.MemoryStorage != ncfg.MemoryStorage {\n\t\treturn errors.New(\"storage type can not be updated\")\n\t}\n\t// Direct and Sourcing classify the consumer for its whole lifetime, which the\n\t// stream relies on when walking its consumer list, so they can not change.\n\tif cfg.Direct != ncfg.Direct {\n\t\treturn errors.New(\"direct can not be updated\")\n\t}\n\tif cfg.Sourcing != ncfg.Sourcing {\n\t\treturn errors.New(\"sourcing can not be updated\")\n\t}\n\tif cfg.OptStartSeq != ncfg.OptStartSeq {\n\t\treturn errors.New(\"start sequence can not be updated\")\n\t}\n\tif cfg.OptStartTime != nil && ncfg.OptStartTime != nil {\n\t\t// Both have start times set, compare them directly:\n\t\tif !cfg.OptStartTime.Equal(*ncfg.OptStartTime) {\n\t\t\treturn errors.New(\"start time can not be updated\")\n\t\t}\n\t} else if cfg.OptStartTime != nil || ncfg.OptStartTime != nil {\n\t\t// At least one start time is set and the other is not\n\t\treturn errors.New(\"start time can not be updated\")\n\t}\n\tif cfg.AckPolicy != ncfg.AckPolicy {\n\t\treturn errors.New(\"ack policy can not be updated\")\n\t}\n\tif cfg.ReplayPolicy != ncfg.ReplayPolicy {\n\t\treturn errors.New(\"replay policy can not be updated\")\n\t}\n\tif cfg.Heartbeat != ncfg.Heartbeat {\n\t\treturn errors.New(\"heart beats can not be updated\")","sourceCodeStart":2531,"sourceCodeEnd":2567,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/consumer.go#L2531-L2567","documentation":"OptStartSeq (optional starting sequence for DeliverByStartSequence) is treated as immutable: once a consumer is created with a starting sequence, it cannot be changed. checkNewConsumerConfig rejects updates where cfg.OptStartSeq != ncfg.OptStartSeq.","triggerScenarios":"js.UpdateConsumer on an existing durable created with OptStartSeq set (DeliverPolicy DeliverByStartSequence) where the update supplies a different sequence number, or adds/removes OptStartSeq.","commonSituations":"Resetting a consumer to an earlier sequence after a data incident; automation recomputing the start sequence on each deploy; repointing a replay consumer at a different starting point.","solutions":["Delete the consumer and recreate it with the new OptStartSeq.","Create a new durable consumer for the new starting sequence.","Remove OptStartSeq from the update so it matches the existing value.","Use an advisory/management flow that recreates consumers on start-position changes."],"exampleFix":"// before\njs.UpdateConsumer(stream, &nats.ConsumerConfig{Durable: \"replay\", OptStartSeq: 42})\n// after\njs.DeleteConsumer(stream, \"replay\")\njs.AddConsumer(stream, &nats.ConsumerConfig{Durable: \"replay\", OptStartSeq: 100})","handlingStrategy":"validation","validationCode":"if existing.OptStartSeq != desired.OptStartSeq {\n\treturn errors.New(\"start sequence cannot be updated; recreate the consumer\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Freeze OptStartSeq in deployment artifacts; do not recompute per deploy","Recreate replay consumers when the start sequence must move","Only update mutable fields (e.g. ack wait, max deliver) on existing durables"],"tags":["jetstream","consumer","start-sequence","immutable-config"],"backgroundTag":"jetstream-consumer-field-immutable","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}