nats-io/nats-server · error · JSPedanticError

max_msg_size must be set to -1

Error message

max_msg_size must be set to -1

What it means

checkStreamCfgLocked (pedantic mode): MaxMsgSize was set below -1 (not 0 or -1). Non-pedantic mode coerces it to -1 (unlimited); pedantic mode refuses the config, requiring the explicit -1 sentinel for unlimited max_msg_size.

Source

Thrown at server/stream.go:1940

			return StreamConfig{}, NewJSPedanticError(fmt.Errorf("max_msgs must be set to -1"))
		}
		cfg.MaxMsgs = -1
	}
	if cfg.MaxMsgsPer == 0 || cfg.MaxMsgsPer < -1 {
		if pedantic && cfg.MaxMsgsPer < -1 {
			return StreamConfig{}, NewJSPedanticError(fmt.Errorf("max_msgs_per_subject must be set to -1"))
		}
		cfg.MaxMsgsPer = -1
	}
	if cfg.MaxBytes == 0 || cfg.MaxBytes < -1 {
		if pedantic && cfg.MaxBytes < -1 {
			return StreamConfig{}, NewJSPedanticError(fmt.Errorf("max_bytes must be set to -1"))
		}
		cfg.MaxBytes = -1
	}
	if cfg.MaxMsgSize == 0 || cfg.MaxMsgSize < -1 {
		if pedantic && cfg.MaxMsgSize < -1 {
			return StreamConfig{}, NewJSPedanticError(fmt.Errorf("max_msg_size must be set to -1"))
		}
		cfg.MaxMsgSize = -1
	}
	if cfg.MaxConsumers == 0 || cfg.MaxConsumers < -1 {
		if pedantic && cfg.MaxConsumers < -1 {
			return StreamConfig{}, NewJSPedanticError(fmt.Errorf("max_consumers must be set to -1"))
		}
		cfg.MaxConsumers = -1
	}
	if cfg.MaxAge < 0 {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("max age can not be negative"))
	}
	if cfg.MaxAge != 0 && cfg.MaxAge < 100*time.Millisecond {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("max age needs to be >= 100ms"))
	}

	if cfg.Duplicates == 0 && cfg.Mirror == nil && len(cfg.Sources) == 0 {
		maxWindow := StreamDefaultDuplicatesWindow

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Set MaxMsgSize explicitly to -1 for no per-message size limit
  2. Set MaxMsgSize to a positive byte value (e.g. 1048576) matching your payload budget
  3. Run without pedantic validation to allow silent normalization

Example fix

// before
StreamConfig{Name: "ORDERS", MaxMsgSize: 0}
// after
StreamConfig{Name: "ORDERS", MaxMsgSize: 2097152}
Defensive patterns

Strategy: validation

Validate before calling

if cfg.MaxMsgSize == 0 || cfg.MaxMsgSize < -1 {
    return fmt.Errorf("MaxMsgSize must be -1 (unlimited) or positive; got %d", cfg.MaxMsgSize)
}

Try / catch

if err != nil {
    var apiErr *nats.APIError
    if errors.As(err, &apiErr) && apiErr.ErrorCode == nats.ErrorCodeJetStreamInvalidStreamConfig {
        cfg.MaxMsgSize = -1
        err = createStream(cfg)
    }
}

Prevention

When it happens

Trigger: Stream create/update API in pedantic mode with MaxMsgSize=0 or a negative value other than -1; e.g. pulling the limit from an environment variable that defaulted to 0 or -2. server/stream.go:1940.

Common situations: Max message size inherited from NATS core's max_payload logic but left unset; env-var parsing failures yielding 0; strict validation turned on in clustered deployments.

Related errors


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