hashicorp/nomad · error

No Keyloader object to perform LoadKeyPair

Error message

No Keyloader object to perform LoadKeyPair

What it means

LoadKeyPair loads CertFile/KeyFile through the pluggable KeyLoader. If CertFile and KeyFile are both set but Config.KeyLoader is nil, there is no object to perform the load, so this error is returned. It is a configuration/initialization bug: TLS cert loading was requested but the loader dependency was never injected.

Source

Thrown at helper/tlsutil/config.go:204

	// Read certificates and return an error if no valid certificates were
	// found. Unfortunately it is very difficult to return meaningful
	// errors as PEM files are extremely permissive.
	if !pool.AppendCertsFromPEM(data) {
		return fmt.Errorf("Failed to parse any valid certificates in CA file: %s", c.CAFile)
	}

	return nil
}

// LoadKeyPair is used to open and parse a certificate and key file
func (c *Config) LoadKeyPair() (*tls.Certificate, error) {
	if c.CertFile == "" || c.KeyFile == "" {
		return nil, nil
	}

	if c.KeyLoader == nil {
		return nil, fmt.Errorf("No Keyloader object to perform LoadKeyPair")
	}

	cert, err := c.KeyLoader.LoadKeyPair(c.CertFile, c.KeyFile)
	if err != nil {
		return nil, fmt.Errorf("Failed to load cert/key pair: %v", err)
	}
	return cert, err
}

// OutgoingTLSConfig generates a TLS configuration for outgoing
// requests. It will return a nil config if this configuration should
// not use TLS for outgoing connections. Provides a callback to
// fetch certificates, allowing for reloading on the fly.
func (c *Config) OutgoingTLSConfig() (*tls.Config, error) {
	// If VerifyServerHostname is true, that implies VerifyOutgoing
	if c.VerifyServerHostname {
		c.VerifyOutgoing = true
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set Config.KeyLoader to the library's default key loader before calling LoadKeyPair.
  2. If no cert/key pair is needed, clear CertFile/KeyFile (LoadKeyPair then returns nil, nil).
  3. Check how the Config is built/decoded to ensure the KeyLoader field is populated (it is not a serialized field).
  4. In tests, use the same initialization helper production code uses instead of a zero-value Config.

Example fix

// before
cfg := &tlsutil.Config{CertFile: cert, KeyFile: key}
cert, err := cfg.LoadKeyPair() // No Keyloader object
// after
cfg := &tlsutil.Config{CertFile: cert, KeyFile: key, KeyLoader: &tlsutil.FileKeyLoader{}}
cert, err := cfg.LoadKeyPair()
Defensive patterns

Strategy: validation

Validate before calling

func (c *tlsutil.Config) validate() error {
    if c.CertFile != "" && c.KeyFile != "" && c.KeyLoader == nil {
        return errors.New("CertFile/KeyFile set but KeyLoader is nil; set a KeyLoader")
    }
    return nil
}
// call cfg.validate() before LoadKeyPair / TLS config generation

Type guard

func hasUsableKeyLoader(c *tlsutil.Config) bool {
    return c.CertFile == "" || c.KeyFile == "" || c.KeyLoader != nil
}

Try / catch

cert, err := cfg.LoadKeyPair()
if err != nil && err.Error() == "No Keyloader object to perform LoadKeyPair" {
    return fmt.Errorf("tls config not initialized: %w", err)
}

Prevention

When it happens

Trigger: Building a Config with both CertFile and KeyFile set and calling LoadKeyPair (directly or via OutgoingTLSConfig/IncomingTLSConfig) while KeyLoader remains nil — typically a hand-constructed Config instead of one initialized with the default loader.

Common situations: Constructing tlsutil.Config{} manually in code or tests and forgetting to set KeyLoader; upgrading library versions where the default loader is no longer auto-populated; copying config structs field-by-field and dropping the loader.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/fc3fb869850e0bde. Report an issue: GitHub.