{"record":{"id":"1fdbc1e80e5403d4","repo":"nats-io/nats-server","slug":"max-expires-must-not-be-negative","errorCode":null,"errorMessage":"max_expires must not be negative","messagePattern":"max_expires must not be negative","errorType":"validation","errorClass":"ApiError","httpStatus":null,"severity":"error","filePath":"server/consumer.go","lineNumber":640,"sourceCode":"\t\t\treturn NewJSPedanticError(errors.New(\"max_waiting must not be negative\"))\n\t\t}\n\t\tconfig.MaxWaiting = 0\n\t}\n\tif config.MaxAckPending < -1 {\n\t\tif pedantic {\n\t\t\treturn NewJSPedanticError(errors.New(\"max_ack_pending must be set to -1\"))\n\t\t}\n\t\tconfig.MaxAckPending = -1\n\t}\n\tif config.MaxRequestBatch < 0 {\n\t\tif pedantic {\n\t\t\treturn NewJSPedanticError(errors.New(\"max_batch must not be negative\"))\n\t\t}\n\t\tconfig.MaxRequestBatch = 0\n\t}\n\tif config.MaxRequestExpires < 0 {\n\t\tif pedantic {\n\t\t\treturn NewJSPedanticError(errors.New(\"max_expires must not be negative\"))\n\t\t}\n\t\tconfig.MaxRequestExpires = 0\n\t}\n\tif config.MaxRequestMaxBytes < 0 {\n\t\tif pedantic {\n\t\t\treturn NewJSPedanticError(errors.New(\"max_bytes must not be negative\"))\n\t\t}\n\t\tconfig.MaxRequestMaxBytes = 0\n\t}\n\tif config.Heartbeat < 0 {\n\t\tif pedantic {\n\t\t\treturn NewJSPedanticError(errors.New(\"idle_heartbeat must not be negative\"))\n\t\t}\n\t\tconfig.Heartbeat = 0\n\t}\n\tif config.InactiveThreshold < 0 {\n\t\tif pedantic {\n\t\t\treturn NewJSPedanticError(errors.New(\"inactive_threshold must not be negative\"))","sourceCodeStart":622,"sourceCodeEnd":658,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/consumer.go#L622-L658","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'.","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."],"exampleFix":"// before\ncc := nats.ConsumerConfig{MaxRequestExpires: -1} // invalid\n// after\ncc := nats.ConsumerConfig{MaxRequestExpires: 0} // no limit","handlingStrategy":"validation","validationCode":"if cfg.MaxRequestExpires < 0 { return errors.New(\"max_expires must not be negative\") }","typeGuard":null,"tryCatchPattern":"if _, err := js.AddConsumer(stream, &cc); err != nil && strings.Contains(err.Error(), \"max_expires\") {\n    cc.MaxRequestExpires = 0\n    _, err = js.AddConsumer(stream, &cc)\n}","preventionTips":["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."],"tags":["jetstream","consumer-config","validation","duration"],"backgroundTag":"invalid-argument-value","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}