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 errMQTTAckPipelineStopped

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. 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
  2. Fix JetStream performance/health: check stream info, raft/quorum state, disk latency, and increase resources
  3. Increase jsa.timeout (server mqtt js ack timeout config) if latency is legitimately high but bounded
  4. 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

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


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/59aa3418e32dd765. Report an issue: GitHub.