{"record":{"id":"0ed059216c0780ff","repo":"nats-io/nats-server","slug":"10069","errorCode":"10069","errorMessage":"change to limits violates consumers: %s","messagePattern":"change to limits violates consumers: (.+?)","errorType":"error_code","errorClass":"JSStreamUpdateError","httpStatus":500,"severity":"error","filePath":"server/jetstream_cluster.go","lineNumber":10324,"sourceCode":"\t// In the event that some of the stream-level limits have changed, yell appropriately\n\t// if any of the consumers exceed that limit.\n\toldInactiveThreshold, newInactiveThreshold := osa.Config.ConsumerLimits.InactiveThreshold, newCfg.ConsumerLimits.InactiveThreshold\n\toldMaxAckPending, newMaxAckPending := osa.Config.ConsumerLimits.MaxAckPending, newCfg.ConsumerLimits.MaxAckPending\n\tupdateLimits := (newInactiveThreshold > 0 && oldInactiveThreshold != newInactiveThreshold) ||\n\t\t(newMaxAckPending > 0 && oldMaxAckPending != newMaxAckPending)\n\tif updateLimits {\n\t\tvar errorConsumers []string\n\t\tfor ca := range js.consumerAssignmentsOrInflightSeq(acc.Name, newCfg.Name) {\n\t\t\tif ca.Config == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (newInactiveThreshold > 0 && ca.Config.InactiveThreshold > newInactiveThreshold) ||\n\t\t\t\t(newMaxAckPending > 0 && ca.Config.MaxAckPending > newMaxAckPending) {\n\t\t\t\terrorConsumers = append(errorConsumers, ca.Name)\n\t\t\t}\n\t\t}\n\t\tif len(errorConsumers) > 0 {\n\t\t\terr := fmt.Errorf(\"change to limits violates consumers: %s\", strings.Join(errorConsumers, \", \"))\n\t\t\tresp.Error = NewJSStreamUpdateError(err)\n\t\t\ts.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Check for a move request.\n\tvar isMoveRequest bool\n\tif lPeerSet := len(peerSet); lPeerSet > 0 {\n\t\tisMoveRequest = true\n\t} else {\n\t\tisMoveRequest = newCfg.Placement != nil && !reflect.DeepEqual(osa.Config.Placement, newCfg.Placement)\n\t}\n\n\t// Check for replica changes.\n\tisReplicaChange := newCfg.Replicas != osa.Config.Replicas\n\n\t// Combining a move and a scale in a single update is not allowed.","sourceCodeStart":10306,"sourceCodeEnd":10342,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/jetstream_cluster.go#L10306-L10342","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Update or delete the consumers named in the message so they comply with the new limits first.","Apply the stream limits change after adjusting consumer MaxAckPending/InactiveThreshold values.","If the change is not essential, keep the current limits and only tighten limits on new consumers."],"exampleFix":"// before: tighten stream limits while consumer exceeds them\njs.UpdateStream(nc, streamName, StreamConfig{MaxAckPending: 100})\n// after: fix consumers first\nfor _, c := range violatingConsumers {\n    cc.MaxAckPending = 100\n    js.UpdateConsumer(nc, streamName, cc)\n}\njs.UpdateStream(nc, streamName, StreamConfig{MaxAckPending: 100})","handlingStrategy":"validation","validationCode":"// before updating stream limits, check consumers comply\nfor _, cc := range existingConsumers {\n    if newMaxAckPending > 0 && cc.MaxAckPending > newMaxAckPending {\n        // update consumer first\n    }\n    if newInactiveThreshold > 0 && cc.InactiveThreshold > newInactiveThreshold {\n        // update consumer first\n    }\n}","typeGuard":null,"tryCatchPattern":"_, err := js.UpdateStream(nc, cfg)\nvar apiErr *nats.APIError\nif errors.As(err, &apiErr) && apiErr.ErrorCode == 10069 {\n    // parse violating consumer names from err, fix them, retry\n}","preventionTips":["List consumers of a stream before tightening its limits.","Update consumers to comply before applying stream limit changes.","Test limit changes on staging streams with identical consumers.","Automate consumer audits after any stream config change."],"tags":["jetstream","stream","consumer","limits","api-error"],"backgroundTag":"limits-violate-consumers","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}