nats-io/nats-server · error · ApiError
max_expires must not be negative
Error message
max_expires must not be negative
What it means
A JetStream pedantic validation error thrown when MaxRequestExpires (the maximum allowed expires duration on a pull request) is negative. Durations cannot be negative; pedantic mode rejects instead of clamping to 0 (no limit) at server/consumer.go:640.
Source
Thrown at server/consumer.go:640
return NewJSPedanticError(errors.New("max_waiting must not be negative"))
}
config.MaxWaiting = 0
}
if config.MaxAckPending < -1 {
if pedantic {
return NewJSPedanticError(errors.New("max_ack_pending must be set to -1"))
}
config.MaxAckPending = -1
}
if config.MaxRequestBatch < 0 {
if pedantic {
return NewJSPedanticError(errors.New("max_batch must not be negative"))
}
config.MaxRequestBatch = 0
}
if config.MaxRequestExpires < 0 {
if pedantic {
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"))View on GitHub (pinned to 3a66a489d2)
Solutions
- Set MaxRequestExpires to a positive duration (e.g. 5 * time.Minute) or 0 for no limit.
- Clamp computed durations: if d < 0 { d = 0 } before assignment.
- Replace -1 'unlimited' convention with 0 for this field.
- Validate the config client-side with Validate(true) before sending.
Example fix
// before
cc := nats.ConsumerConfig{MaxRequestExpires: -1} // invalid
// after
cc := nats.ConsumerConfig{MaxRequestExpires: 0} // no limit Defensive patterns
Strategy: validation
Validate before calling
if cfg.MaxRequestExpires < 0 { return errors.New("max_expires must not be negative") } Try / catch
if _, err := js.AddConsumer(stream, &cc); err != nil && strings.Contains(err.Error(), "max_expires") {
cc.MaxRequestExpires = 0
_, err = js.AddConsumer(stream, &cc)
} Prevention
- Never use negative durations for expires fields; 0 means no limit/default.
- Check duration arithmetic for possible underflow before assigning.
- Sanitize env/flag-parsed durations with a negative check.
When it happens
Trigger: ConsumerConfig.MaxRequestExpires < 0 (e.g. -30 * time.Second) passed during consumer create/update with pedantic validation; typically from a subtraction of time.Duration values or a misconfigured TTL flag.
Common situations: Computing a remaining TTL that underflows to negative; operators setting expires limits as -1 believing it means 'unlimited' (use 0 instead); generated config from tooling defaulting to -1 for 'not set'.
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
- got corrupted escaped character
- max_ack_pending must be set to -1
- max_batch must not be negative
- max_bytes must not be negative
- idle_heartbeat must not be negative
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/1fdbc1e80e5403d4.
Report an issue: GitHub.