nats-io/nats-server · error · ApiError

inactive_threshold must not be negative

Error message

inactive_threshold must not be negative

What it means

A JetStream pedantic validation error thrown when InactiveThreshold (the time after which an inactive ephemeral consumer is deleted) is negative. It must be 0 (use default) or a positive duration; pedantic mode rejects the config instead of clamping to 0 at server/consumer.go:658.

Source

Thrown at server/consumer.go:658

			return NewJSPedanticError(errors.New("max_expires must not be negative"))
		}
		config.MaxRequestExpires = 0
	}
	if config.MaxRequestMaxBytes < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("max_bytes must not be negative"))
		}
		config.MaxRequestMaxBytes = 0
	}
	if config.Heartbeat < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("idle_heartbeat must not be negative"))
		}
		config.Heartbeat = 0
	}
	if config.InactiveThreshold < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("inactive_threshold must not be negative"))
		}
		config.InactiveThreshold = 0
	}
	if config.PinnedTTL < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("priority_timeout must not be negative"))
		}
		config.PinnedTTL = 0
	}

	// 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
	}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Set InactiveThreshold to 0 for the default or a positive duration (e.g. 5 * time.Minute).
  2. Clamp computed durations to >= 0 before assignment.
  3. If 'never expire' is intended, use a very large duration instead of a negative one.
  4. Validate the config client-side with Validate(true).

Example fix

// before
cc := nats.ConsumerConfig{InactiveThreshold: -1 * time.Minute}
// after
cc := nats.ConsumerConfig{InactiveThreshold: 5 * time.Minute}
Defensive patterns

Strategy: validation

Validate before calling

if cfg.InactiveThreshold < 0 { return errors.New("inactive_threshold must not be negative") }

Try / catch

if _, err := js.AddConsumer(stream, &cc); err != nil && strings.Contains(err.Error(), "inactive_threshold must not be negative") {
    cc.InactiveThreshold = 0
    _, err = js.AddConsumer(stream, &cc)
}

Prevention

When it happens

Trigger: ConsumerConfig.InactiveThreshold < 0 passed on consumer create/update with pedantic validation, e.g. InactiveThreshold: -time.Minute or from stream ConsumerLimits-derived values that went negative.

Common situations: Duration subtraction (lastSeen - now) underflow; '-1 = never delete' misconception (use a very large duration instead); templated configs with negative defaults.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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