slackhq/nebula · error

input did not contain a valid PEM encoded block

Error message

input did not contain a valid PEM encoded block

What it means

DecryptAndUnmarshalSigningPrivateKey calls pem.Decode on the input bytes; when no valid PEM block can be parsed (pem.Decode returns nil), it returns this error. The library requires the input to start with a PEM-encoded encrypted Nebula private key block. Any corrupted, truncated, or non-PEM input fails immediately before the key banner is even checked.

Source

Thrown at cert/crypto.go:260

	return &Argon2Parameters{
		version:     params.Version,
		Memory:      params.Memory,
		Parallelism: uint8(params.Parallelism),
		Iterations:  params.Iterations,
		salt:        params.Salt,
	}, nil

}

// DecryptAndUnmarshalSigningPrivateKey will try to pem decode and decrypt an Ed25519/ECDSA private key with
// the given passphrase, returning any other bytes b or an error on failure
func DecryptAndUnmarshalSigningPrivateKey(passphrase, b []byte) (Curve, []byte, []byte, error) {
	var curve Curve

	k, r := pem.Decode(b)
	if k == nil {
		return curve, nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
	}

	switch k.Type {
	case EncryptedEd25519PrivateKeyBanner:
		curve = Curve_CURVE25519
	case EncryptedECDSAP256PrivateKeyBanner:
		curve = Curve_P256
	default:
		return curve, nil, r, fmt.Errorf("bytes did not contain a proper nebula encrypted Ed25519/ECDSA private key banner")
	}

	ned, err := UnmarshalNebulaEncryptedData(k.Bytes)
	if err != nil {
		return curve, nil, r, err
	}

	var bytes []byte
	switch ned.EncryptionMetadata.EncryptionAlgorithm {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Verify the input bytes actually contain a PEM block starting with '-----BEGIN NEBULA ED25519 ENCRYPTED PRIVATE KEY-----' (or the ECDSA P256 equivalent) and are not empty
  2. Check you are passing the encrypted private key file, not the certificate, CA file, or config file
  3. If the file was produced by EncryptAndMarshalSigningPrivateKey, re-export/re-copy it without text-mangling (binary/base64-safe transfer)
  4. Regenerate the key with nebula-cert if the source file is truly corrupt

Example fix

// before
b, _ := os.ReadFile("config.yaml") // wrong file
k, _, _, err := cert.DecryptAndUnmarshalSigningPrivateKey(pass, b)
// after
b, _ := os.ReadFile("host.key") // the encrypted Nebula private key
k, _, _, err := cert.DecryptAndUnmarshalSigningPrivateKey(pass, b)
Defensive patterns

Strategy: validation

Validate before calling

func hasPEM(b []byte) bool {
	blk, _ := pem.Decode(b)
	return blk != nil
}
// call only if hasPEM(keyBytes)

Type guard

func isValidEncryptedSigningKeyPEM(b []byte) bool {
	blk, _ := pem.Decode(b)
	if blk == nil {
		return false
	}
	return blk.Type == cert.EncryptedEd25519PrivateKeyBanner || blk.Type == cert.EncryptedECDSAP256PrivateKeyBanner
}

Try / catch

curve, key, rest, err := cert.DecryptAndUnmarshalSigningPrivateKey(pass, b)
if err != nil {
	if strings.Contains(err.Error(), "valid PEM encoded block") {
		return fmt.Errorf("key file is not PEM-encoded; check file path and contents: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling DecryptAndUnmarshalSigningPrivateKey(passphrase, b) where b is not parseable as PEM: empty input, plain-text key material, a passphrase typed instead of the key file, a PEM block with invalid base64 or missing BEGIN/END lines, or output of a different function (e.g. a certificate) passed by mistake.

Common situations: Config files pointing at the wrong file (config vs key file), key files mangled by copy/paste or secret-manager templating that produced empty content, Windows line-ending or whitespace corruption, or passing an unencrypted PEM (e.g. from OpenSSL) instead of a Nebula encrypted key produced by EncryptAndMarshalSigningPrivateKey.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/d582036c546bae60. Report an issue: GitHub.