nats-io/nats-server · error · ApiError
priority_timeout must not be negative
Error message
priority_timeout must not be negative
What it means
A JetStream pedantic validation error thrown when PinnedTTL (how long a client may hold a pinned priority-group pull consumer) is negative. The field is the internal representation of the public priority_timeout and must be 0 or positive; pedantic mode rejects rather than clamping at server/consumer.go:664.
Source
Thrown at server/consumer.go:664
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
}
// 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]View on GitHub (pinned to 3a66a489d2)
Solutions
- Set PinnedTTL / priority_timeout to a positive duration (e.g. 2 * time.Minute) or 0 to disable pinning.
- Clamp computed TTLs to >= 0 before assignment.
- Only set PinnedTTL when PriorityPolicy/priority groups are configured; otherwise leave 0.
- Validate client-side with Validate(true) before the request.
Example fix
// before
cc := nats.ConsumerConfig{
PriorityPolicy: nats.PriorityPinnedClient,
PinnedTTL: -30 * time.Second,
}
// after
cc := nats.ConsumerConfig{
PriorityPolicy: nats.PriorityPinnedClient,
PinnedTTL: 2 * time.Minute,
} Defensive patterns
Strategy: validation
Validate before calling
if cfg.PinnedTTL < 0 { return errors.New("priority_timeout must not be negative") }
if cfg.PinnedTTL > 0 && cfg.PriorityPolicy == 0 { return errors.New("PinnedTTL requires a PriorityPolicy") } Try / catch
if _, err := js.AddConsumer(stream, &cc); err != nil && strings.Contains(err.Error(), "priority_timeout") {
cc.PinnedTTL = 0
_, err = js.AddConsumer(stream, &cc)
} Prevention
- Set PinnedTTL only together with PriorityPolicy/priority groups and always positive.
- Use 0 to disable pinning, never a negative TTL.
- Cover priority-group configs with a client-side validation test.
When it happens
Trigger: ConsumerConfig.PinnedTTL < 0 on consumer create/update with pedantic validation, typically when using priority groups (PriorityGroups/PriorityPolicy) and setting priority_timeout to a negative duration.
Common situations: Misreading priority_timeout as a signed 'sticky forever' flag; duration arithmetic errors in priority-group scheduling code; copied config templates with negative placeholder TTLs.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- got corrupted escaped character
- max_ack_pending must be set to -1
- max_batch must not be negative
- max_expires must not be negative
- max_bytes must not be negative
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/57660d661ab4fd05.
Report an issue: GitHub.