nats-io/nats-server · error
sourcing can not be updated
Error message
sourcing can not be updated
What it means
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.
Source
Thrown at server/consumer.go:2546
// Check if new consumer config allowed vs old.
func (acc *Account) checkNewConsumerConfig(cfg, ncfg *ConsumerConfig) error {
if reflect.DeepEqual(cfg, ncfg) {
return nil
}
// Something different, so check since we only allow certain things to be updated.
if cfg.DeliverPolicy != ncfg.DeliverPolicy {
return errors.New("deliver policy can not be updated")
}
if cfg.MemoryStorage != ncfg.MemoryStorage {
return errors.New("storage type can not be updated")
}
// Direct and Sourcing classify the consumer for its whole lifetime, which the
// stream relies on when walking its consumer list, so they can not change.
if cfg.Direct != ncfg.Direct {
return errors.New("direct can not be updated")
}
if cfg.Sourcing != ncfg.Sourcing {
return errors.New("sourcing can not be updated")
}
if cfg.OptStartSeq != ncfg.OptStartSeq {
return errors.New("start sequence can not be updated")
}
if cfg.OptStartTime != nil && ncfg.OptStartTime != nil {
// Both have start times set, compare them directly:
if !cfg.OptStartTime.Equal(*ncfg.OptStartTime) {
return errors.New("start time can not be updated")
}
} else if cfg.OptStartTime != nil || ncfg.OptStartTime != nil {
// At least one start time is set and the other is not
return errors.New("start time can not be updated")
}
if cfg.AckPolicy != ncfg.AckPolicy {
return errors.New("ack policy can not be updated")
}
if cfg.ReplayPolicy != ncfg.ReplayPolicy {
return errors.New("replay policy can not be updated")View on GitHub (pinned to 3a66a489d2)
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.
Example fix
// before
js.UpdateConsumer(stream, &nats.ConsumerConfig{Durable: "aggregator", Sources: []*nats.ConsumerSource{{Stream: "V2"}}})
// after
js.DeleteConsumer(stream, "aggregator")
js.AddConsumer(stream, &nats.ConsumerConfig{Durable: "aggregator", Sources: []*nats.ConsumerSource{{Stream: "V2"}}}) Defensive patterns
Strategy: validation
Validate before calling
if !reflect.DeepEqual(existing.Sources, desired.Sources) && sourcingChanged(existing, desired) {
return errors.New("sourcing cannot be updated; delete and recreate the consumer")
} Prevention
- Treat source/mirror definitions as immutable in tooling
- Recreate consumers on stream-retargeting migrations
- Diff sources against ConsumerInfo before update
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- deliver policy can not be updated
- storage type can not be updated
- direct can not be updated
- start sequence can not be updated
- start time can not be updated
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/93c7f5042bf98f00.
Report an issue: GitHub.