nats-io/nats-server · error

missing 'key_file' in TLS configuration

Error message

missing 'key_file' in TLS configuration

What it means

During TLS config building (parseTLS/GenericTLSConfig), if cert_file is set but key_file is empty, the server returns this error. A TLS certificate without its matching private key cannot form a usable key pair, so the server rejects the configuration.

Source

Thrown at server/opts.go:5835

}

// 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
		}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Add key_file pointing to the PEM private key alongside cert_file
  2. If using a cert store instead, remove cert_file and set cert_store
  3. Verify both paths exist and are readable by the server process

Example fix

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

Strategy: validation

Validate before calling

if cfg.TLS.CertFile != "" && cfg.TLS.KeyFile == "" {
  return fmt.Errorf("cert_file set but key_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 cert_file but omitting key_file, e.g. tc.CertFile != "" && tc.KeyFile == "" in server/opts.go:5835.

Common situations: Incomplete TLS sections in nats-server config; keys stored elsewhere (e.g. hardware/keystore) with only the cert path given; copy-paste dropping the key line.

Understand the failure class

Related errors


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