nats-io/nats-server · warning

JetStream did not acknowledge the QoS1 message within %v (se

Error message

JetStream did not acknowledge the QoS1 message within %v (server is shutting down); failing the connection, the client will re-send unacknowledged PUBLISH packets on reconnect

What it means

During server shutdown, the JetStream subscriber for MQTT QoS1 acks stops waiting: when the per-ack timer expires while the server is terminating, it fails the pending ack with this verbose error and the client connection is failed. The message tells the operator the client will recover by resending unacknowledged PUBLISH packets after reconnecting.

Source

Thrown at server/mqtt.go:4685

	pipe := c.mqtt.acks
	if pipe == nil {
		if pipe = s.mqttPipelineStart(c, jsa); pipe == nil {
			// Server is shutting down, complete synchronously.
			t := time.NewTimer(jsa.timeout)
			defer t.Stop()
			select {
			case err := <-ack.done:
				if err != nil {
					return err
				}
				c.mu.Lock()
				trace := c.trace
				c.mu.Unlock()
				c.mqttEnqueuePubResponse(mqttPacketPubAck, ack.pi, trace)
				return nil
			case <-t.C:
				jsa.replies.Delete(ack.reply)
				return fmt.Errorf("JetStream did not acknowledge the QoS1 message within %v "+
					"(server is shutting down); failing the connection, "+
					"the client will re-send unacknowledged PUBLISH packets on reconnect", jsa.timeout)
			case <-s.quitCh:
				// The reply may never come; do not hold up the shutdown.
				jsa.replies.Delete(ack.reply)
				return ErrServerNotRunning
			}
		}
		c.mqtt.acks = pipe
	}

	return pipe.push(ack)
}

// Admits an entry into the pipeline. Rejects it once the pipeline is
// stopped: an entry admitted after the connection-close drain
// (mqttHandleClosedClient -> pipe.shutdown) would strand its reply
// registration in the account-scoped jsa.replies. readLoop only.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. This is expected behavior during shutdown; ensure clients use clean-session=false and QoS1 so they re-send unacked PUBLISH on reconnect
  2. Drain MQTT traffic before shutdown (stop publishers, wait for in-flight acks) to avoid the error
  3. Reduce JS ack latency (storage tuning, quorum health) so acks complete before shutdown completes
  4. Use nats-server's graceful shutdown wait time long enough for in-flight acks to settle
Defensive patterns

Strategy: retry

Try / catch

client.OnConnectionLost = func(_ mqtt.Client, err error) {
  if strings.Contains(err.Error(), "server is shutting down") {
    scheduleReconnectWithBackoff() // client resends unacked QoS1 PUBLISH automatically
  }
}

Prevention

When it happens

Trigger: A QoS1 PUBLISH is pending a JetStream ack and the server's quitCh fires; the ack timer then expires before JetStream responds, producing this shutdown-path error instead of the normal timeout message.

Common situations: Rolling restarts or graceful shutdown with in-flight QoS1 MQTT traffic, long JS ack latency coinciding with shutdown, operators draining a cluster with connected MQTT publishers.

Related errors


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