slackhq/nebula · error
key was not %d bytes, is invalid Ed25519 private key
Error message
key was not %d bytes, is invalid Ed25519 private key
What it means
The PEM block was recognized as an Ed25519 signing private key (Ed25519PrivateKeyBanner), but the decoded bytes are not ed25519.PrivateKeySize (64) bytes long. Ed25519 private keys are 64 bytes (seed + public half); anything else means the key body is damaged or was encoded incorrectly.
Source
Thrown at cert/pem.go:266
}
return k.Bytes, r, curve, nil
}
func UnmarshalSigningPrivateKeyFromPEM(b []byte) ([]byte, []byte, Curve, error) {
k, r := pem.Decode(b)
if k == nil {
return nil, r, 0, fmt.Errorf("input did not contain a valid PEM encoded block")
}
var curve Curve
switch k.Type {
case EncryptedEd25519PrivateKeyBanner:
return nil, nil, Curve_CURVE25519, ErrPrivateKeyEncrypted
case EncryptedECDSAP256PrivateKeyBanner:
return nil, nil, Curve_P256, ErrPrivateKeyEncrypted
case Ed25519PrivateKeyBanner:
curve = Curve_CURVE25519
if len(k.Bytes) != ed25519.PrivateKeySize {
return nil, r, 0, fmt.Errorf("key was not %d bytes, is invalid Ed25519 private key", ed25519.PrivateKeySize)
}
case ECDSAP256PrivateKeyBanner:
curve = Curve_P256
if len(k.Bytes) != 32 {
return nil, r, 0, fmt.Errorf("key was not 32 bytes, is invalid ECDSA P256 private key")
}
default:
return nil, r, 0, fmt.Errorf("bytes did not contain a proper Ed25519/ECDSA private key banner")
}
return k.Bytes, r, curve, nil
}
View on GitHub (pinned to dd8f660c0a)
Solutions
- Regenerate the CA signing key with nebula-cert ca so a full 64-byte Ed25519 private key is emitted
- Decode the PEM body and verify it is exactly ed25519.PrivateKeySize (64) bytes before calling
- If you only have the 32-byte seed, expand it with ed25519.NewKeyFromSeed before PEM-encoding
Example fix
// before
pem.Encode(file, &pem.Block{Type: banner, Bytes: seed}) // 32 bytes
// after
full := ed25519.NewKeyFromSeed(seed)
pem.Encode(file, &pem.Block{Type: banner, Bytes: full}) // 64 bytes Defensive patterns
Strategy: validation
Validate before calling
blk, _ := pem.Decode(data)
if blk != nil && blk.Type == "NEBULA ED25519 SIGNING PRIVATE KEY" && len(blk.Bytes) != ed25519.PrivateKeySize {
return fmt.Errorf("Ed25519 signing key must be 64 bytes, got %d", len(blk.Bytes))
} Type guard
func isValidEd25519SigningKey(b []byte) bool {
blk, _ := pem.Decode(b)
return blk != nil && blk.Type == "NEBULA ED25519 SIGNING PRIVATE KEY" && len(blk.Bytes) == ed25519.PrivateKeySize
} Try / catch
key, _, _, err := nebula.UnmarshalSigningPrivateKeyFromPEM(raw)
if err != nil {
return fmt.Errorf("Ed25519 signing key is malformed (expected 64 bytes): %w", err)
} Prevention
- Never store just the 32-byte seed under a full-private-key banner
- If you only have a seed, expand it with ed25519.NewKeyFromSeed before encoding
- Regenerate the CA rather than reconstructing its key by hand
When it happens
Trigger: Call UnmarshalSigningPrivateKeyFromPEM with a block of type Ed25519PrivateKeyBanner whose k.Bytes length != 64, e.g. a 32-byte seed pasted under the full-key banner or a truncated file.
Common situations: Storing only the 32-byte seed but labeling it as the full private key, truncation during copy/paste or through line-wrapping tools, or regenerating the CA and mixing old banner with new (short) material.
Related errors
- key was not 32 bytes, is invalid ECDSA P256 private key
- ErrInvalidPEMEd25519PublicKeyBanner
- ErrInvalidPEMEd25519PrivateKeyBanner
- key was not 64 bytes, is invalid ed25519 private key
- key was not %d bytes, is invalid ed25519 private key
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/f70d49c2773936a9.
Report an issue: GitHub.