hyperledger/fabric · error
failed to decode PEM block from %s
Error message
failed to decode PEM block from %s
What it means
loadPrivateKey reads the key file and calls pem.Decode; if the file content is not a valid PEM block (nil result), it throws this error including the file path. The Signer requires the private key in PEM form to parse it via parsePrivateKey.
Source
Thrown at cmd/common/signer/signer.go:116
// Fabric only supports ECDSA and ed25519 at the moment.
case *ecdsa.PrivateKey:
digest := util.ComputeSHA256(msg)
return signECDSA(si.key.(*ecdsa.PrivateKey), digest)
case ed25519.PrivateKey:
return ed25519.Sign(si.key.(ed25519.PrivateKey), msg), nil
default:
return nil, errors.Errorf("found unknown private key type (%T) in msg signing", key)
}
}
func loadPrivateKey(file string) (crypto.PrivateKey, error) {
b, err := os.ReadFile(file)
if err != nil {
return nil, errors.WithStack(err)
}
bl, _ := pem.Decode(b)
if bl == nil {
return nil, errors.Errorf("failed to decode PEM block from %s", file)
}
key, err := parsePrivateKey(bl.Bytes)
if err != nil {
return nil, err
}
return key, nil
}
// Based on crypto/tls/tls.go but modified for Fabric:
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
// OpenSSL 1.0.0 generates PKCS#8 keys.
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
switch key := key.(type) {
// Fabric only supports ECDSA at the moment.
case *ecdsa.PrivateKey:
return key, nil
case ed25519.PrivateKey:
return key, nilView on GitHub (pinned to 2736b63f8f)
Solutions
- Convert the key to PEM: openssl pkey -inform DER -in key.der -out key.pem (or openssl ec -in key.sec1.der ... )
- Verify the file contains '-----BEGIN ... PRIVATE KEY-----' at the top
- Fix SignerConfig.KeyPath in config to point at the actual PEM private key
- Re-download/re-export the key from the MSP directory
Example fix
// before openssl pkcs8 -topk8 -inform DER -in key.der -out key.pem -nocrypt // after: config signer.key: /path/to/key.pem (PEM-encoded)
Defensive patterns
Strategy: validation
Validate before calling
b, _ := os.ReadFile(keyPath)
if blk, _ := pem.Decode(b); blk == nil {
return fmt.Errorf("%s is not PEM-encoded", keyPath)
} Type guard
func isPEMPrivateKey(b []byte) bool {
blk, _ := pem.Decode(b)
return blk != nil && strings.Contains(blk.Type, "PRIVATE KEY")
} Try / catch
s, err := signer.NewSigner(keyPath, idPath)
if err != nil {
if strings.Contains(err.Error(), "failed to decode PEM block") {
return fmt.Errorf("key %s not PEM; convert: openssl pkey -inform DER", keyPath)
}
return err
} Prevention
- Convert DER keys to PEM at enrollment time
- Check file starts with '-----BEGIN' before use
- Verify KeyPath points to the key, not the cert
When it happens
Trigger: NewSigner with a key file that is raw DER, empty, an error page, base64 without headers, or corrupted/truncated PEM; wrong path passed as SignerConfig.KeyPath so the file read succeeds but content isn't PEM.
Common situations: Key exported in DER (PKCS#8/SEC1 binary) form; key file truncated by failed download; pointing KeyPath at the certificate or some other non-PEM file; trailing whitespace-only file.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- enrollment certificate isn't a valid PEM block
- %s is mandatory and cannot be empty
- enrollment certificate should be a certificate, got a %s ins
- failed to parse private key: %v
- could not create channel %s sub-group config
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/2d78bcdff9b92a21.
Report an issue: GitHub.