hyperledger/fabric · critical

unable to load key at '%s'

Error message

unable to load key at '%s'

What it means

loadDeliverServiceConfig reads the TLS private key file (peer.tls.key.file or the deliver-service-specific key override) and panics with this wrapped error if os.ReadFile fails — typically because the file doesn't exist, isn't readable, or the configured path is empty/relative-wrong. This runs during GlobalConfig initialization, so it crashes the peer at startup.

Source

Thrown at core/deliverservice/config.go:201

	c.SecOpts = comm.SecureOptions{
		UseTLS:            viper.GetBool("peer.tls.enabled"),
		RequireClientCert: viper.GetBool("peer.tls.clientAuthRequired"),
	}

	if c.SecOpts.RequireClientCert {
		certFile := config.GetPath("peer.tls.clientCert.file")
		if certFile == "" {
			certFile = config.GetPath("peer.tls.cert.file")
		}

		keyFile := config.GetPath("peer.tls.clientKey.file")
		if keyFile == "" {
			keyFile = config.GetPath("peer.tls.key.file")
		}

		keyPEM, err := os.ReadFile(keyFile)
		if err != nil {
			panic(errors.WithMessagef(err, "unable to load key at '%s'", keyFile))
		}
		c.SecOpts.Key = keyPEM
		certPEM, err := os.ReadFile(certFile)
		if err != nil {
			panic(errors.WithMessagef(err, "unable to load cert at '%s'", certFile))
		}
		c.SecOpts.Certificate = certPEM
	}

	overridesMap, err := LoadOverridesMap()
	if err != nil {
		panic(err)
	}

	c.OrdererEndpointOverrides = overridesMap

	policyKey := "peer.deliveryclient.policy"
	policyMissing := !viper.IsSet(policyKey)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Verify the key file path in core.yaml (peer.tls.key.file / deliver service key config) and ensure the file exists at that exact path.
  2. Check file permissions/readability for the user running the peer process.
  3. In containerized deployments, confirm the secret volume is mounted and CORE_PEER_TLS_KEYFILE points to the mounted path.
  4. Use absolute paths in config instead of relative ones to avoid CWD dependence.

Example fix

// before (core.yaml)
peer:
  tls:
    key.file: tls/server.key   // relative, file absent
// after
peer:
  tls:
    key.file: /etc/hyperledger/fabric/tls/server.key  // verified existing, readable
Defensive patterns

Strategy: validation

Validate before calling

keyPath := viper.GetString("peer.tls.key.file")
if keyPath == "" {
    return errors.New("peer.tls.key.file not configured")
}
if fi, err := os.Stat(keyPath); err != nil || fi.IsDir() {
    return fmt.Errorf("TLS key file missing: %s", keyPath)
}
if _, err := os.ReadFile(keyPath); err != nil {
    return fmt.Errorf("TLS key unreadable: %w", err)
}

Try / catch

// This path panics rather than returns, so preflight before peer startup:
// run the validation above as a startup preflight; on panic, log the wrapped
// cause (errors.Cause) to see the underlying os error (ENOENT vs EACCES).

Prevention

When it happens

Trigger: TLS enabled for the delivery service but the key file path resolves to a nonexistent file, an empty string (config key not set), or a file the peer user cannot read.

Common situations: core.yaml misconfiguration of peer.tls.key.file; missing mounted secret in Kubernetes/Docker; wrong working directory with a relative path; file permissions changed after volume mount; TLS enabled but key file never provisioned.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/f3dad0f9b72a7c2f. Report an issue: GitHub.