nats-io/nats-server · warning

JetStream message size exceeds limits for '%s > %s'

Error message

JetStream message size exceeds limits for '%s > %s'

What it means

During JetStream publish routing, the server pre-emptively checks incoming messages against the stream's MaxMsgSize (falling back to account/server max payload limits). If the header or payload length exceeds the limit, it logs 'JetStream message size exceeds limits for <account > stream>' and returns JSStreamMessageExceedsMaximumError to the publisher instead of letting the message flow through and fail later.

Source

Thrown at server/jetstream_cluster.go:12123

	// Check here pre-emptively if we have exceeded our account limits.
	if exceeded, err := jsa.wouldExceedLimits(st, tierName, r, csubject, hdr, msg); exceeded {
		if err == nil {
			err = NewJSAccountResourcesExceededError()
		}
		s.RateLimitWarnf("JetStream account limits exceeded for '%s': %s", jsa.acc().GetName(), err.Error())
		if canRespond {
			var resp = &JSPubAckResponse{PubAck: &PubAck{Stream: name}}
			resp.Error = err
			response, _ = json.Marshal(resp)
			outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, nil, response, nil, 0))
		}
		return err
	}

	// Check msgSize if we have a limit set there. Again this works if it goes through but better to be pre-emptive.
	// Subtract to prevent against overflows.
	if maxMsgSize >= 0 && (len(hdr) > maxMsgSize || len(msg) > maxMsgSize-len(hdr)) {
		err := fmt.Errorf("JetStream message size exceeds limits for '%s > %s'", jsa.acc().Name, name)
		s.RateLimitWarnf("%s", err.Error())
		if canRespond {
			var resp = &JSPubAckResponse{PubAck: &PubAck{Stream: name}}
			resp.Error = NewJSStreamMessageExceedsMaximumError()
			response, _ = json.Marshal(resp)
			outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, nil, response, nil, 0))
		}
		return err
	}

	// Proceed with proposing this message.

	// We only use mset.clseq for clustering and in case we run ahead of actual commits.
	// Check if we need to set initial value here
	mset.clMu.Lock()
	if mset.clseq == 0 || mset.clseq < lseq+mset.clfs {
		lseq = recalculateClusteredSeq(mset, true)
	}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Reduce message size: split or compress the payload before publishing.
  2. Raise the stream's MaxMsgSize via stream update (still bounded by server max_payload).
  3. Raise the server's max_payload in nats.conf if the limit originates there (requires cluster-wide consistent value and restart).
  4. Check client-side with len(data) + header size before publishing to fail fast.

Example fix

// before
js.Publish("ORDERS.new", bigPayload) // > MaxMsgSize
// after
if len(bigPayload) > maxAllowed {
    bigPayload = compress(bigPayload) // or chunk and publish parts
}
js.Publish("ORDERS.new", bigPayload)
Defensive patterns

Strategy: validation

Validate before calling

// Go client: pre-check size before publishing
info, _ := js.StreamInfo(streamName)
maxAllowed := int(info.Config.MaxMsgSize)
if maxAllowed >= 0 && len(msg)+len(hdr) > maxAllowed {
    return fmt.Errorf("payload %d exceeds stream max %d", len(msg), maxAllowed)
}

Try / catch

ak, err := js.Publish(subj, payload)
var apiErr *nats.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode == 10059 { // exceeds max
    payload = chunkOrCompress(payload)
    ak, err = js.Publish(subj, payload)
}

Prevention

When it happens

Trigger: Publishing a message (with `js.Publish`, `nc.Request` to a stream-mapped subject, or direct raw publish) whose total size (headers included) exceeds the stream's MaxMsgSize or the server's max_payload; the check is len(msg) > maxMsgSize - len(hdr), so large headers alone can trip it.

Common situations: Producers sending attachments or batched payloads larger than the configured limit; a stream created with a tighter MaxMsgSize than the server default; base64/JSON envelopes inflating payloads past the limit.

Related errors


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