nats-io/nats-server · error

missing 'cert_file' in TLS configuration

Error message

missing 'cert_file' in TLS configuration

What it means

GenTLSConfig emits this when a TLS configuration needs a certificate but tc.CertFile is empty — the switch that validates cert_file/cert_store combinations found no cert_file supplied where one is required (e.g. next to a configured key_file). It fires before any TLS handshake, purely from inspecting the config struct, so the fix is always in the configuration file.

Source

Thrown at server/opts.go:5837

// GenTLSConfig loads TLS related configuration parameters.
func GenTLSConfig(tc *TLSConfigOpts) (*tls.Config, error) {
	// Create the tls.Config from our options before including the certs.
	// It will determine the cipher suites that we prefer.
	// FIXME(dlc) change if ARM based.
	config := tls.Config{
		MinVersion:         tls.VersionTLS12,
		CipherSuites:       tc.Ciphers,
		CurvePreferences:   tc.CurvePreferences,
		InsecureSkipVerify: tc.Insecure,
	}

	switch {
	case tc.CertFile != _EMPTY_ && tc.CertStore != certstore.STOREEMPTY:
		return nil, certstore.ErrConflictCertFileAndStore
	case tc.CertFile != _EMPTY_ && tc.KeyFile == _EMPTY_:
		return nil, fmt.Errorf("missing 'key_file' in TLS configuration")
	case tc.CertFile == _EMPTY_ && tc.KeyFile != _EMPTY_:
		return nil, fmt.Errorf("missing 'cert_file' in TLS configuration")
	case tc.CertFile != _EMPTY_ && tc.KeyFile != _EMPTY_:
		// Now load in cert and private key
		cert, err := tls.LoadX509KeyPair(tc.CertFile, tc.KeyFile)
		if err != nil {
			return nil, fmt.Errorf("error parsing X509 certificate/key pair: %v", err)
		}
		cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
		if err != nil {
			return nil, fmt.Errorf("error parsing certificate: %v", err)
		}
		config.Certificates = []tls.Certificate{cert}
	case tc.CertStore != certstore.STOREEMPTY:
		err := certstore.TLSConfig(tc.CertStore, tc.CertMatchBy, tc.CertMatch, tc.CaCertsMatch, tc.CertMatchSkipInvalid, &config)
		if err != nil {
			return nil, err
		}
	case tc.Certificates != nil:
		// Multiple certificate support.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Add cert_file pointing to the PEM certificate chain
  2. Ensure the value is non-empty after any env-var expansion
  3. If intending a cert store, remove key_file and configure cert_store instead

Example fix

// before
tls { key_file: "/certs/server.key" }
// after
tls {
  cert_file: "/certs/server.pem"
  key_file: "/certs/server.key"
}
Defensive patterns

Strategy: validation

Validate before calling

if cfg.TLS.KeyFile != "" && cfg.TLS.CertFile == "" {
  return fmt.Errorf("key_file set but cert_file missing")
}

Try / catch

if err := validateTLSPairs(cfg.TLS); err != nil { log.Fatalf("tls config: %v", err) }

Prevention

When it happens

Trigger: TLS block specifying key_file without cert_file (tc.CertFile == "" && tc.KeyFile != ""), typically in nats-server config 'tls' sections.

Common situations: Config template placeholders left unfilled; cert path typo causing empty resolution elsewhere; partial migration from another server's config format.

Understand the failure class

Related errors


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