{"record":{"id":"93c7f5042bf98f00","repo":"nats-io/nats-server","slug":"sourcing-can-not-be-updated","errorCode":null,"errorMessage":"sourcing can not be updated","messagePattern":"sourcing can not be updated","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/consumer.go","lineNumber":2546,"sourceCode":"// Check if new consumer config allowed vs old.\nfunc (acc *Account) checkNewConsumerConfig(cfg, ncfg *ConsumerConfig) error {\n\tif reflect.DeepEqual(cfg, ncfg) {\n\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\")","sourceCodeStart":2528,"sourceCodeEnd":2564,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/consumer.go#L2528-L2564","documentation":"Like Direct, the Sourcing (source/multiple sources) definition classifies a consumer for its whole lifetime and cannot be modified after creation. checkNewConsumerConfig rejects any difference between the existing cfg.Sourcing and the proposed ncfg.Sourcing. This covers both adding sourcing to a plain consumer and altering source streams/subjects.","triggerScenarios":"js.UpdateConsumer on an existing durable where the Source/Source(s) configuration differs — e.g. adding nats.ConsumerSource{Stream: \"OTHER\"}, changing filter subjects, or removing sources from a sourced consumer.","commonSituations":"Retargeting a mirror/source to a new stream during a migration; adding a second source for multi-stream aggregation; infra-as-code diffs that mutate the sources block of an existing durable.","solutions":["Delete and recreate the consumer with the new sourcing configuration.","Create a new consumer with a new durable name pointing at the new sources, then retire the old one.","Revert the sourcing change to match the existing config.","Verify current sources with ConsumerInfo before issuing updates."],"exampleFix":"// before\njs.UpdateConsumer(stream, &nats.ConsumerConfig{Durable: \"aggregator\", Sources: []*nats.ConsumerSource{{Stream: \"V2\"}}})\n// after\njs.DeleteConsumer(stream, \"aggregator\")\njs.AddConsumer(stream, &nats.ConsumerConfig{Durable: \"aggregator\", Sources: []*nats.ConsumerSource{{Stream: \"V2\"}}})","handlingStrategy":"validation","validationCode":"if !reflect.DeepEqual(existing.Sources, desired.Sources) && sourcingChanged(existing, desired) {\n\treturn errors.New(\"sourcing cannot be updated; delete and recreate the consumer\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat source/mirror definitions as immutable in tooling","Recreate consumers on stream-retargeting migrations","Diff sources against ConsumerInfo before update"],"tags":["jetstream","consumer","sourcing","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"}