{"record":{"id":"df42aadc9503bac6","repo":"nats-io/nats-server","slug":"max-ack-pending-must-be-set-if-it-s-configured-in","errorCode":null,"errorMessage":"max_ack_pending must be set if it's configured in stream limits","messagePattern":"max_ack_pending must be set if it's configured in stream limits","errorType":"validation","errorClass":"ApiError","httpStatus":null,"severity":"error","filePath":"server/consumer.go","lineNumber":686,"sourceCode":"\n\t// Set to default if not specified.\n\tif config.DeliverSubject == _EMPTY_ && config.MaxWaiting == 0 {\n\t\tconfig.MaxWaiting = JSWaitQueueDefaultMax\n\t}\n\t// Setup proper default for ack wait if we are in explicit ack mode.\n\tif config.AckWait == 0 && (config.AckPolicy == AckExplicit || config.AckPolicy == AckAll) {\n\t\tconfig.AckWait = JsAckWaitDefault\n\t}\n\t// If BackOff was specified that will override the AckWait and the MaxDeliver.\n\tif len(config.BackOff) > 0 {\n\t\tif pedantic && config.AckWait != config.BackOff[0] {\n\t\t\treturn NewJSPedanticError(errors.New(\"first backoff value has to equal batch AckWait\"))\n\t\t}\n\t\tconfig.AckWait = config.BackOff[0]\n\t}\n\tif config.MaxAckPending == 0 {\n\t\tif pedantic && streamCfg.ConsumerLimits.MaxAckPending > 0 {\n\t\t\treturn NewJSPedanticError(errors.New(\"max_ack_pending must be set if it's configured in stream limits\"))\n\t\t}\n\t\tconfig.MaxAckPending = streamCfg.ConsumerLimits.MaxAckPending\n\t}\n\tif config.InactiveThreshold == 0 {\n\t\tif pedantic && streamCfg.ConsumerLimits.InactiveThreshold > 0 {\n\t\t\treturn NewJSPedanticError(errors.New(\"inactive_threshold must be set if it's configured in stream limits\"))\n\t\t}\n\t\tconfig.InactiveThreshold = streamCfg.ConsumerLimits.InactiveThreshold\n\t}\n\t// Set proper default for max ack pending if we are ack explicit and none has been set.\n\tif config.MaxAckPending == 0 && config.AckPolicy != AckNone {\n\t\tackPending := JsDefaultMaxAckPending\n\t\tif lim.MaxAckPending > 0 && lim.MaxAckPending < ackPending {\n\t\t\tackPending = lim.MaxAckPending\n\t\t}\n\t\tif accLim.MaxAckPending > 0 && accLim.MaxAckPending < ackPending {\n\t\t\tackPending = accLim.MaxAckPending\n\t\t}","sourceCodeStart":668,"sourceCodeEnd":704,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/consumer.go#L668-L704","documentation":"A JetStream pedantic validation error thrown when the stream's ConsumerLimits define a MaxAckPending > 0 but the consumer config leaves MaxAckPending unset (0). Because 0 would otherwise inherit a value exceeding the stream's limit, pedantic mode requires the consumer to state it explicitly at server/consumer.go:686.","triggerScenarios":"Creating/updating a consumer on a stream whose ConsumerLimits.MaxAckPending > 0 while ConsumerConfig.MaxAckPending == 0 with pedantic validation enabled, e.g. stream created with MaxConsumers limits after a server upgrade that introduced ConsumerLimits.","commonSituations":"After upgrading the server and adding ConsumerLimits to existing streams, previously working consumer templates (which relied on defaults) start failing; multi-tenant setups where admins cap consumers but client code creates consumers without setting the field.","solutions":["Set MaxAckPending explicitly in the consumer config to a value <= the stream's ConsumerLimits.MaxAckPending.","Read the stream info first and mirror its ConsumerLimits.MaxAckPending into the consumer config.","Relax the stream's ConsumerLimits.MaxAckPending to 0 if the cap is no longer needed.","Update consumer-creation templates to always set MaxAckPending when streams enforce limits."],"exampleFix":"// before\nsi, _ := js.StreamInfo(\"ORDERS\")\ncc := nats.ConsumerConfig{Durable: \"w\"} // MaxAckPending unset\njs.AddConsumer(\"ORDERS\", &cc)\n// after\ncc := nats.ConsumerConfig{\n    Durable:       \"w\",\n    MaxAckPending: si.Config.ConsumerLimits.MaxAckPending,\n}\njs.AddConsumer(\"ORDERS\", &cc)","handlingStrategy":"validation","validationCode":"si, err := js.StreamInfo(stream)\nif err != nil { return err }\nif lim := si.Config.ConsumerLimits.MaxAckPending; lim > 0 && cc.MaxAckPending == 0 {\n    cc.MaxAckPending = lim\n}","typeGuard":null,"tryCatchPattern":"if _, err := js.AddConsumer(stream, &cc); err != nil && strings.Contains(err.Error(), \"max_ack_pending must be set\") {\n    si, _ := js.StreamInfo(stream)\n    cc.MaxAckPending = si.Config.ConsumerLimits.MaxAckPending\n    _, err = js.AddConsumer(stream, &cc)\n}","preventionTips":["Fetch stream info and honor ConsumerLimits when creating consumers.","After adding ConsumerLimits to streams, audit all consumer-creation templates.","Always set MaxAckPending explicitly rather than relying on defaults."],"tags":["jetstream","consumer-config","stream-limits","ack-pending"],"backgroundTag":"consumer-limits-violation","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}