{"record":{"id":"d131a636883554f1","repo":"nats-io/nats-server","slug":"flow-control-ack-policy-heartbeat-needs-to-be-1s","errorCode":null,"errorMessage":"flow control ack policy heartbeat needs to be 1s","messagePattern":"flow control ack policy heartbeat needs to be 1s","errorType":"validation","errorClass":"JSStreamInvalidConfigError","httpStatus":null,"severity":"error","filePath":"server/consumer.go","lineNumber":793,"sourceCode":"\t\t}\n\t}\n\tif config.AckWait < 0 {\n\t\treturn NewJSConsumerAckWaitNegativeError()\n\t}\n\n\t// Ack Flow Control policy requires push-based flow-controlled consumer.\n\tif config.AckPolicy == AckFlowControl {\n\t\tif config.DeliverSubject == _EMPTY_ {\n\t\t\treturn NewJSConsumerAckFCRequiresPushError()\n\t\t}\n\t\tif !config.FlowControl {\n\t\t\treturn NewJSConsumerAckFCRequiresFCError()\n\t\t}\n\t\t// We currently limit using heartbeat of 1s, since those are used for ephemeral sourcing consumers as well.\n\t\t// We could decide to relax this in the future, but need to be careful to not allow a heartbeat larger\n\t\t// than the stalled source timeout.\n\t\tif config.Heartbeat != sourceHealthHB {\n\t\t\treturn NewJSStreamInvalidConfigError(fmt.Errorf(\"flow control ack policy heartbeat needs to be 1s\"))\n\t\t}\n\t\tif config.MaxAckPending <= 0 {\n\t\t\treturn NewJSConsumerAckFCRequiresMaxAckPendingError()\n\t\t}\n\t\tif config.AckWait != 0 || len(config.BackOff) > 0 {\n\t\t\treturn NewJSConsumerAckFCRequiresNoAckWaitError()\n\t\t}\n\t\tif config.MaxDeliver > 0 {\n\t\t\treturn NewJSConsumerAckFCRequiresNoMaxDeliverError()\n\t\t}\n\t}\n\n\t// Check if we have a BackOff defined that MaxDeliver is within range etc.\n\tif lbo := len(config.BackOff); lbo > 0 && config.MaxDeliver != -1 && lbo > config.MaxDeliver {\n\t\treturn NewJSConsumerMaxDeliverBackoffError()\n\t}\n\n\tif len(config.Description) > JSMaxDescriptionLen {","sourceCodeStart":775,"sourceCodeEnd":811,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/consumer.go#L775-L811","documentation":"When a consumer's AckPolicy is flow-control-based (acked flow control, paired with source/sourcing consumers), the server requires config.Heartbeat to be exactly sourceHealthHB (1s), because heartbeats are also used to detect stalled ephemeral sourcing consumers. Any other heartbeat interval is rejected with this JSStreamInvalidConfigError at validation time.","triggerScenarios":"Creating/updating a consumer with AckPolicy set to the flow-control ack policy and Heartbeat set to anything other than 1s (e.g. 2s, 500ms, or via the client library's Heartbeat/IdleHeartbeat field).","commonSituations":"Tuning heartbeats for slower networks and picking 2-5s while also enabling flow control acks; copying sourcing consumer examples that omit Heartbeat (zero is also != 1s).","solutions":["Set Heartbeat (client-side IdleHeartbeat) to exactly 1s (time.Second / \"1s\" in JSON config).","If a different heartbeat is required, switch AckPolicy off the flow-control policy (e.g. explicit/all) so this constraint no longer applies.","Also satisfy the companion constraints checked nearby: MaxAckPending > 0 and AckWait == 0 with empty BackOff."],"exampleFix":"// before\ncc := &nats.ConsumerConfig{AckPolicy: nats.AckFCPolicy, Heartbeat: 2 * time.Second}\n// after\ncc := &nats.ConsumerConfig{AckPolicy: nats.AckFCPolicy, Heartbeat: time.Second}","handlingStrategy":"validation","validationCode":"if cc.AckPolicy == nats.AckFCPolicy && cc.Heartbeat != time.Second {\n    return fmt.Errorf(\"flow control ack policy requires Heartbeat == 1s, got %v\", cc.Heartbeat)\n}\nif cc.AckPolicy == nats.AckFCPolicy && cc.MaxAckPending <= 0 {\n    return fmt.Errorf(\"flow control ack policy requires MaxAckPending > 0\")\n}","typeGuard":"func fcAckConfigValid(hb time.Duration, maxAckPending int) bool {\n    return hb == time.Second && maxAckPending > 0\n}","tryCatchPattern":"_, err := js.AddConsumer(stream, cc)\nvar cerr *nats.APIError\nif errors.As(err, &cerr) && strings.Contains(cerr.Description, \"heartbeat needs to be 1s\") {\n    cc.Heartbeat = time.Second\n    return js.AddConsumer(stream, cc)\n}","preventionTips":["Always pair AckFCPolicy with IdleHeartbeat: time.Second","Remember AckWait must be 0 and BackOff empty when using flow control acks","Prefer explicit/all ack policies if you need custom heartbeat intervals"],"tags":["jetstream","consumer-config","flow-control"],"backgroundTag":"invalid-consumer-config","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}