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

  1. Identify the consumers named in the message and delete or recreate them before tightening limits.
  2. Loosen the limits so all existing consumers remain valid, then tighten after consumers have caught up.
  3. 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

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


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/1f7ed52787240fc3. Report an issue: GitHub.