nats-io/nats-server · error · JSStreamUpdateError

10069

10069

Error message

change to limits violates consumers: %s

What it means

JSStreamUpdateError (API error 10069) with message 'change to limits violates consumers: %s' is returned by the stream UPDATE handler when a proposed change to stream limits (max inactive threshold semantics / new MaxAckPending limits being tightened) would make existing consumers of that stream invalid. The comma-joined consumer names listed in the message are the consumers that would violate the new limits.

Source

Thrown at server/jetstream_cluster.go:10324

	// In the event that some of the stream-level limits have changed, yell appropriately
	// if any of the consumers exceed that limit.
	oldInactiveThreshold, newInactiveThreshold := osa.Config.ConsumerLimits.InactiveThreshold, newCfg.ConsumerLimits.InactiveThreshold
	oldMaxAckPending, newMaxAckPending := osa.Config.ConsumerLimits.MaxAckPending, newCfg.ConsumerLimits.MaxAckPending
	updateLimits := (newInactiveThreshold > 0 && oldInactiveThreshold != newInactiveThreshold) ||
		(newMaxAckPending > 0 && oldMaxAckPending != newMaxAckPending)
	if updateLimits {
		var errorConsumers []string
		for ca := range js.consumerAssignmentsOrInflightSeq(acc.Name, newCfg.Name) {
			if ca.Config == nil {
				continue
			}
			if (newInactiveThreshold > 0 && ca.Config.InactiveThreshold > newInactiveThreshold) ||
				(newMaxAckPending > 0 && ca.Config.MaxAckPending > newMaxAckPending) {
				errorConsumers = append(errorConsumers, ca.Name)
			}
		}
		if len(errorConsumers) > 0 {
			err := fmt.Errorf("change to limits violates consumers: %s", strings.Join(errorConsumers, ", "))
			resp.Error = NewJSStreamUpdateError(err)
			s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
			return
		}
	}

	// Check for a move request.
	var isMoveRequest bool
	if lPeerSet := len(peerSet); lPeerSet > 0 {
		isMoveRequest = true
	} else {
		isMoveRequest = newCfg.Placement != nil && !reflect.DeepEqual(osa.Config.Placement, newCfg.Placement)
	}

	// Check for replica changes.
	isReplicaChange := newCfg.Replicas != osa.Config.Replicas

	// Combining a move and a scale in a single update is not allowed.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Update or delete the consumers named in the message so they comply with the new limits first.
  2. Apply the stream limits change after adjusting consumer MaxAckPending/InactiveThreshold values.
  3. If the change is not essential, keep the current limits and only tighten limits on new consumers.

Example fix

// before: tighten stream limits while consumer exceeds them
js.UpdateStream(nc, streamName, StreamConfig{MaxAckPending: 100})
// after: fix consumers first
for _, c := range violatingConsumers {
    cc.MaxAckPending = 100
    js.UpdateConsumer(nc, streamName, cc)
}
js.UpdateStream(nc, streamName, StreamConfig{MaxAckPending: 100})
Defensive patterns

Strategy: validation

Validate before calling

// before updating stream limits, check consumers comply
for _, cc := range existingConsumers {
    if newMaxAckPending > 0 && cc.MaxAckPending > newMaxAckPending {
        // update consumer first
    }
    if newInactiveThreshold > 0 && cc.InactiveThreshold > newInactiveThreshold {
        // update consumer first
    }
}

Try / catch

_, err := js.UpdateStream(nc, cfg)
var apiErr *nats.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode == 10069 {
    // parse violating consumer names from err, fix them, retry
}

Prevention

When it happens

Trigger: Issuing `$JS.API.STREAM.UPDATE.*` (or `js.UpdateStream`) where the new config tightens limits (e.g. lower max ack pending / inactive threshold bounds) while one or more existing consumers currently exceed those limits.

Common situations: Downsizing a stream's limits in production while long-lived consumers are attached; automation that copies a template config onto streams with pre-existing consumers.

Related errors


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