golang/go · error
ed25519: bad Ed25519ph context length: {l}
Error message
ed25519: bad Ed25519ph context length: {l} What it means
Returned by SignPH when the context string exceeds 255 bytes. RFC 8032 encodes the context length in a single leading byte, so 255 is the hard ceiling for Ed25519ph and Ed25519ctx.
Source
Thrown at src/crypto/internal/fips140/ed25519/ed25519.go:193
fips140.RecordApproved()
return signWithDom(signature, priv, message, domPrefixPure, "")
}
func SignPH(priv *PrivateKey, message []byte, context string) ([]byte, error) {
// Outline the function body so that the returned signature can be
// stack-allocated.
signature := make([]byte, signatureSize)
return signPH(signature, priv, message, context)
}
func signPH(signature []byte, priv *PrivateKey, message []byte, context string) ([]byte, error) {
fipsSelfTest()
fips140.RecordApproved()
if l := len(message); l != sha512Size {
return nil, errors.New("ed25519: bad Ed25519ph message hash length: " + strconv.Itoa(l))
}
if l := len(context); l > 255 {
return nil, errors.New("ed25519: bad Ed25519ph context length: " + strconv.Itoa(l))
}
return signWithDom(signature, priv, message, domPrefixPh, context), nil
}
func SignCtx(priv *PrivateKey, message []byte, context string) ([]byte, error) {
// Outline the function body so that the returned signature can be
// stack-allocated.
signature := make([]byte, signatureSize)
return signCtx(signature, priv, message, context)
}
func signCtx(signature []byte, priv *PrivateKey, message []byte, context string) ([]byte, error) {
fipsSelfTest()
// FIPS 186-5 specifies Ed25519 and Ed25519ph (with context), but not Ed25519ctx.
fips140.RecordNonApproved()
// Note that per RFC 8032, Section 5.1, the context SHOULD NOT be empty.
if l := len(context); l > 255 {
return nil, errors.New("ed25519: bad Ed25519ctx context length: " + strconv.Itoa(l))View on GitHub (pinned to b6b368adc5)
Solutions
- Keep the context string to <= 255 bytes; ideally a short stable identifier.
- If you need more domain separation, hash the longer blob to 32 bytes and use that as the context (or fold it into the message).
- Pass an empty context if no context binding is required for Ed25519ph.
Example fix
// before sig, err := ed25519.SignPH(priv, digest, longPolicyDocument) // after ctxID := sha256.Sum256([]byte(longPolicyDocument)) sig, err := ed25519.SignPH(priv, digest, string(ctxID[:]))
Defensive patterns
Strategy: validation
Validate before calling
if len(context) > 255 {
h := sha256.Sum256([]byte(context))
context = string(h[:])
}
sig, err := ed25519.SignPH(priv, digest, context) Try / catch
sig, err := ed25519.SignPH(priv, digest, context)
if err != nil {
if strings.Contains(err.Error(), "bad Ed25519ph context length") {
return nil, fmt.Errorf("context too long (%d > 255)", len(context))
}
return nil, err
} Prevention
- Use short stable identifiers as context.
- Hash long domain-separation strings to 32 bytes before passing.
- Share the canonical context between signer and verifier.
When it happens
Trigger: Calling fips140/ed25519.SignPH(priv, digest, context) with len(context) > 255 — e.g. passing a full URL, certificate, or arbitrary blob as the context.
Common situations: Stuffing domain-separation metadata, paths, or JSON into the context field; concatenating multiple context values without first hashing them down.
Related errors
- ed25519: bad Ed25519ph message hash length: {l}
- ed25519: bad Ed25519ctx context length: {l}
- ed25519: bad seed length: {l}
- ed25519: bad private key length: {l}
- ed25519: bad public key length: {l}
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/e47fb95965abf0ad.
Report an issue: GitHub.