nats-io/nats-server · error
QoS1 in-flight window is full (%d messages) and JetStream ha
Error message
QoS1 in-flight window is full (%d messages) and JetStream has not acknowledged the oldest message within %v; failing the connection, the client will re-send unacknowledged PUBLISH packets on reconnect
What it means
The MQTT JetStream bridge bounds the number of un-acked QoS1 messages in flight (mqttMaxAcksInFlight). When the window is full, new acks must be enqueued via a bounded select; if the pipe stays full past jsa.timeout, the server fails the connection with this error so the client resends unacknowledged PUBLISH packets on reconnect.
Source
Thrown at server/mqtt.go:4727
return errMQTTAckPipelineStopped
default:
}
enqueued := false
select {
case pipe.q <- ack:
enqueued = true
default:
// Window full: wait for an available slot in the pipeline.
t := time.NewTimer(jsa.timeout)
defer t.Stop()
select {
case pipe.q <- ack:
enqueued = true
case <-pipe.quitCh:
case <-t.C:
jsa.replies.Delete(ack.reply)
return fmt.Errorf("QoS1 in-flight window is full (%d messages) and JetStream has not acknowledged "+
"the oldest message within %v; failing the connection, "+
"the client will re-send unacknowledged PUBLISH packets on reconnect",
mqttMaxAcksInFlight, jsa.timeout)
}
}
if !enqueued {
// Stopped while waiting for a slot, never admitted.
jsa.replies.Delete(ack.reply)
return errMQTTAckPipelineStopped
}
// A stop may have raced the enqueue: the close-time drain could run
// before the entry landed and miss it. Drain again; both drains only
// dequeue and delete, so overlapping is harmless.
select {
case <-pipe.quitCh:
pipe.shutdown()
return errMQTTAckPipelineStoppedView on GitHub (pinned to 3a66a489d2)
Solutions
- Throttle the MQTT publisher's QoS1 rate (client-side in-flight window, e.g. max_inflight in the client) so it doesn't outpace JS acks
- Fix JetStream performance/health: check stream info, raft/quorum state, disk latency, and increase resources
- Increase jsa.timeout (server mqtt js ack timeout config) if latency is legitimately high but bounded
- Verify stream limits (max msgs, max bytes, retention) are not causing ack stalls
Example fix
// before (client) opts.MaxReconnect = 5 // after (client-side QoS1 throttling) opts := mqtt.NewClientOptions().SetWriteTimeout(10*time.Second) mqttClient.SetMaxInflight(64) // stay below server mqttMaxAcksInFlight
Defensive patterns
Strategy: retry
Try / catch
if err := publish(qos1payload); err != nil && strings.Contains(err.Error(), "in-flight window is full") {
time.Sleep(backoff)
resumeFromLastUnacked()
} Prevention
- Throttle client QoS1 publish rate below JetStream's sustained ack throughput
- Keep client in-flight window below the server's mqttMaxAcksInFlight
- Alert on JetStream ack latency and stream limit near-capacity
- Fix storage/quorum problems promptly — backpressure here indicates JS slowness
When it happens
Trigger: An MQTT client publishes QoS1 messages faster than JetStream acks them, filling mqttMaxAcksInFlight slots; the pending ack cannot be enqueued within jsa.timeout because the pipe is full and not draining.
Common situations: JetStream backing store is slow or quorum-degraded, stream limits/maximums block commits, a single aggressive MQTT publisher flooding QoS1 messages, or network partition slowing JS acks.
Related errors
- timeout after %v waiting for the JetStream ack
- JetStream did not acknowledge the QoS1 message within %v (se
- ack wait must be a positive value
- JS API timeout must be a positive value
- mqtt requires JetStream to be enabled if running in standalo
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/59aa3418e32dd765.
Report an issue: GitHub.