nats-io/nats-server · error · ApiError

max_ack_pending must be set if it's configured in stream lim

Error message

max_ack_pending must be set if it's configured in stream limits

What it means

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.

Source

Thrown at server/consumer.go:686

	// Set to default if not specified.
	if config.DeliverSubject == _EMPTY_ && config.MaxWaiting == 0 {
		config.MaxWaiting = JSWaitQueueDefaultMax
	}
	// Setup proper default for ack wait if we are in explicit ack mode.
	if config.AckWait == 0 && (config.AckPolicy == AckExplicit || config.AckPolicy == AckAll) {
		config.AckWait = JsAckWaitDefault
	}
	// If BackOff was specified that will override the AckWait and the MaxDeliver.
	if len(config.BackOff) > 0 {
		if pedantic && config.AckWait != config.BackOff[0] {
			return NewJSPedanticError(errors.New("first backoff value has to equal batch AckWait"))
		}
		config.AckWait = config.BackOff[0]
	}
	if config.MaxAckPending == 0 {
		if pedantic && streamCfg.ConsumerLimits.MaxAckPending > 0 {
			return NewJSPedanticError(errors.New("max_ack_pending must be set if it's configured in stream limits"))
		}
		config.MaxAckPending = streamCfg.ConsumerLimits.MaxAckPending
	}
	if config.InactiveThreshold == 0 {
		if pedantic && streamCfg.ConsumerLimits.InactiveThreshold > 0 {
			return NewJSPedanticError(errors.New("inactive_threshold must be set if it's configured in stream limits"))
		}
		config.InactiveThreshold = streamCfg.ConsumerLimits.InactiveThreshold
	}
	// Set proper default for max ack pending if we are ack explicit and none has been set.
	if config.MaxAckPending == 0 && config.AckPolicy != AckNone {
		ackPending := JsDefaultMaxAckPending
		if lim.MaxAckPending > 0 && lim.MaxAckPending < ackPending {
			ackPending = lim.MaxAckPending
		}
		if accLim.MaxAckPending > 0 && accLim.MaxAckPending < ackPending {
			ackPending = accLim.MaxAckPending
		}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Set MaxAckPending explicitly in the consumer config to a value <= the stream's ConsumerLimits.MaxAckPending.
  2. Read the stream info first and mirror its ConsumerLimits.MaxAckPending into the consumer config.
  3. Relax the stream's ConsumerLimits.MaxAckPending to 0 if the cap is no longer needed.
  4. Update consumer-creation templates to always set MaxAckPending when streams enforce limits.

Example fix

// before
si, _ := js.StreamInfo("ORDERS")
cc := nats.ConsumerConfig{Durable: "w"} // MaxAckPending unset
js.AddConsumer("ORDERS", &cc)
// after
cc := nats.ConsumerConfig{
    Durable:       "w",
    MaxAckPending: si.Config.ConsumerLimits.MaxAckPending,
}
js.AddConsumer("ORDERS", &cc)
Defensive patterns

Strategy: validation

Validate before calling

si, err := js.StreamInfo(stream)
if err != nil { return err }
if lim := si.Config.ConsumerLimits.MaxAckPending; lim > 0 && cc.MaxAckPending == 0 {
    cc.MaxAckPending = lim
}

Try / catch

if _, err := js.AddConsumer(stream, &cc); err != nil && strings.Contains(err.Error(), "max_ack_pending must be set") {
    si, _ := js.StreamInfo(stream)
    cc.MaxAckPending = si.Config.ConsumerLimits.MaxAckPending
    _, err = js.AddConsumer(stream, &cc)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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