{"record":{"id":"f9774be889bdc6cc","repo":"nats-io/nats-server","slug":"inactive-threshold-must-be-set-if-it-s-configured","errorCode":null,"errorMessage":"inactive_threshold must be set if it's configured in stream limits","messagePattern":"inactive_threshold must be set if it's configured in stream limits","errorType":"validation","errorClass":"ApiError","httpStatus":null,"severity":"error","filePath":"server/consumer.go","lineNumber":692,"sourceCode":"\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}\n\t\tconfig.MaxAckPending = ackPending\n\t}\n\t// if applicable set max request batch size\n\tif config.DeliverSubject == _EMPTY_ && config.MaxRequestBatch == 0 && lim.MaxRequestBatch > 0 {\n\t\tif pedantic {\n\t\t\treturn NewJSPedanticError(errors.New(\"max_request_batch must be set if it's JetStream limits are set\"))","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/consumer.go#L674-L710","documentation":"A JetStream pedantic validation error thrown when the stream's ConsumerLimits define an InactiveThreshold > 0 but the consumer config leaves InactiveThreshold unset (0). Pedantic mode requires the consumer to explicitly set it so it does not silently inherit an unexpected auto-cleanup value; thrown at server/consumer.go:692.","triggerScenarios":"Creating/updating a consumer on a stream whose ConsumerLimits.InactiveThreshold > 0 while ConsumerConfig.InactiveThreshold == 0 with pedantic validation, typically after the stream was configured with an inactivity cap for ephemeral consumers.","commonSituations":"Environments where admins added ConsumerLimits.InactiveThreshold to reap abandoned ephemerals, then existing consumer-creation code (which never set the field) fails; migration from non-pedantic clients where the value was silently inherited.","solutions":["Set InactiveThreshold explicitly in the consumer config to a positive duration <= the stream limit.","Fetch stream info and copy its ConsumerLimits.InactiveThreshold into the consumer config.","Clear the stream's ConsumerLimits.InactiveThreshold (set 0) if auto-cleanup is unnecessary.","Update consumer templates to always set InactiveThreshold on streams that enforce it."],"exampleFix":"// before\ncc := nats.ConsumerConfig{\n    Durable:           \"w\",\n    InactiveThreshold: 0,\n}\n// after\ncc := nats.ConsumerConfig{\n    Durable:           \"w\",\n    InactiveThreshold: streamCfg.ConsumerLimits.InactiveThreshold,\n}","handlingStrategy":"validation","validationCode":"si, err := js.StreamInfo(stream)\nif err != nil { return err }\nif lim := si.Config.ConsumerLimits.InactiveThreshold; lim > 0 && cc.InactiveThreshold == 0 {\n    cc.InactiveThreshold = lim\n}","typeGuard":null,"tryCatchPattern":"if _, err := js.AddConsumer(stream, &cc); err != nil && strings.Contains(err.Error(), \"inactive_threshold must be set\") {\n    si, _ := js.StreamInfo(stream)\n    cc.InactiveThreshold = si.Config.ConsumerLimits.InactiveThreshold\n    _, err = js.AddConsumer(stream, &cc)\n}","preventionTips":["Mirror the stream's ConsumerLimits.InactiveThreshold into every consumer config.","Audit consumer-creation code whenever stream-level ConsumerLimits change.","Set InactiveThreshold explicitly for ephemeral consumers instead of relying on inheritance."],"tags":["jetstream","consumer-config","stream-limits","inactive-threshold"],"backgroundTag":"consumer-limits-violation","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}