nats-io/nats-server · error
change to limits violates consumers: %s
Error message
change to limits violates consumers: %s
What it means
When updating stream limits (MaxMsgs, MaxBytes, MaxAge, MaxMsgSize, MaxMsgsPerSubject etc.), JetStream checks every consumer against the new limits; if any consumer's state would be violated (e.g. its redelivery/ack floor exceeds the new retention), the update is rejected listing the offending consumers. As the TODO notes, this is currently a plain error without a parsable JS API error code.
Source
Thrown at server/stream.go:2696
mset.mu.RLock()
clist := make([]*consumer, 0, len(mset.consumers))
for _, c := range mset.consumers {
clist = append(clist, c)
}
mset.mu.RUnlock()
for _, c := range clist {
c.mu.RLock()
name, ccfg := c.name, c.cfg
if (newInactiveThreshold > 0 && ccfg.InactiveThreshold > newInactiveThreshold) ||
(newMaxAckPending > 0 && ccfg.MaxAckPending > newMaxAckPending) {
errorConsumers = append(errorConsumers, name)
}
c.mu.RUnlock()
}
if len(errorConsumers) > 0 {
// TODO(nat): Return a parsable error so that we can surface something
// sensible through the JS API.
return fmt.Errorf("change to limits violates consumers: %s", strings.Join(errorConsumers, ", "))
}
}
jsa.mu.RLock()
if jsa.subjectsOverlap(cfg.Subjects, mset) {
jsa.mu.RUnlock()
return NewJSStreamSubjectOverlapError()
}
jsa.mu.RUnlock()
mset.mu.Lock()
var needsStartingSeqNum map[string]struct{}
if mset.active {
// Check for mirror promotion.
if ocfg.Mirror != nil && cfg.Mirror == nil {
// Only try deleting the sourcing consumer if one wasn't provided to us.
if ocfg.Mirror.Consumer == nil {View on GitHub (pinned to 3a66a489d2)
Solutions
- Identify the consumers named in the message and delete or recreate them before tightening limits.
- Loosen the limits so all existing consumers remain valid, then tighten after consumers have caught up.
- Drain/consumer-delete the affected consumers, apply the update, and re-create consumers with fresh DeliverPolicy.
Defensive patterns
Strategy: validation
Validate before calling
for _, cl := range consumerList {
ci, _ := js.ConsumerInfo(ctx, stream, cl)
if newCfg.MaxAge > 0 && time.Since(ci.Delivered.Last) > newCfg.MaxAge {
// tighten limits after deleting/draining this consumer
}
} Try / catch
if err != nil && strings.Contains(err.Error(), "violates consumers") { /* parse consumer names after 'violates consumers: ' */ } Prevention
- Delete or drain consumers before shrinking MaxMsgs/MaxAge/MaxBytes.
- Tighten limits in stages while consumers catch up.
When it happens
Trigger: UpdateStream with tighter limits while consumers exist whose progress (e.g. consumer sequence, pending state) is beyond what the new limits permit; also triggered when subject overlaps are checked right after this path.
Common situations: Reducing MaxAge or MaxMsgs to clean up disk while long-lived pull consumers still track old messages; shrinking MaxBytes below current consumer state after storage growth incidents.
Related errors
- duplicate window limits are higher than current limits
- JS_STREAM_MSG_DELETE_FAILED
- JS_STREAM_PURGE_FAILED
- JS_STREAM_ROLLUP_FAILED
- consumer not found
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/1f7ed52787240fc3.
Report an issue: GitHub.