golang/go · error
invalid {{ .Element }} encoding
Error message
invalid {{ .Element }} encoding What it means
Generated into each fiat-curve element type (P224/P256/P384/P521) from generate.go:219. Thrown by (*Element).SetBytes when the input is not exactly the curve's element length (ElementLen). This is the field-element parser rejecting wrong-size big-endian scalars/coordinates before any range check.
Source
Thrown at src/crypto/internal/fips140/nistec/fiat/generate.go:219
// rather than happen on the heap.
var out [{{ .Prefix }}ElementLen]byte
return e.bytes(&out)
}
func (e *{{ .Element }}) bytes(out *[{{ .Prefix }}ElementLen]byte) []byte {
var tmp {{ .Prefix }}NonMontgomeryDomainFieldElement
{{ .Prefix }}FromMontgomery(&tmp, &e.x)
{{ .Prefix }}ToBytes(out, (*{{ .Prefix }}UntypedFieldElement)(&tmp))
{{ .Prefix }}InvertEndianness(out[:])
return out[:]
}
// SetBytes sets e = v, where v is a big-endian {{ .BytesLen }}-byte encoding, and returns e.
// If v is not {{ .BytesLen }} bytes or it encodes a value higher than {{ .Prime }},
// SetBytes returns nil and an error, and e is unchanged.
func (e *{{ .Element }}) SetBytes(v []byte) (*{{ .Element }}, error) {
if len(v) != {{ .Prefix }}ElementLen {
return nil, errors.New("invalid {{ .Element }} encoding")
}
// Check for non-canonical encodings (p + k, 2p + k, etc.) by comparing to
// the encoding of -1 mod p, so p - 1, the highest canonical encoding.
var minusOneEncoding = new({{ .Element }}).Sub(
new({{ .Element }}), new({{ .Element }}).One()).Bytes()
if subtle.ConstantTimeLessOrEqBytes(v, minusOneEncoding) == 0 {
return nil, errors.New("invalid {{ .Element }} encoding")
}
var in [{{ .Prefix }}ElementLen]byte
copy(in[:], v)
{{ .Prefix }}InvertEndianness(in[:])
var tmp {{ .Prefix }}NonMontgomeryDomainFieldElement
{{ .Prefix }}FromBytes((*{{ .Prefix }}UntypedFieldElement)(&tmp), &in)
{{ .Prefix }}ToMontgomery(&e.x, &tmp)
return e, nil
}View on GitHub (pinned to b6b368adc5)
Solutions
- Right-pad/truncate the value to exactly ElementLen bytes for the target curve before calling SetBytes.
- When converting from big.Int, use a fixed-size FillBytes call and check its returned length.
- Confirm the byte slice is raw big-endian, not hex/base64.
- Dispatch on curve ID and use the matching ElementLen constant.
Example fix
// before
x, err := new(fiat.P256Element).SetBytes(v) // v from big.Int.Bytes(), short by 1
// after
buf := make([]byte, fiat.P256ElementLen)
if !bi.FillBytes(buf) { return errors.New("value too large for P-256") }
x, err := new(fiat.P256Element).SetBytes(buf) Defensive patterns
Strategy: validation
Validate before calling
// Pad to the curve's ElementLen before SetBytes.
buf := make([]byte, fiat.P256ElementLen)
if !bi.FillBytes(buf) {
return errors.New("value too large for field")
}
e, err := new(fiat.P256Element).SetBytes(buf) Type guard
func isCurveElementBytes(b []byte, elLen int) bool { return len(b) == elLen } Try / catch
e, err := new(fiat.P256Element).SetBytes(v)
if err != nil {
return fmt.Errorf("field element rejected (len=%d, want=%d): %w", len(v), fiat.P256ElementLen, err)
} Prevention
- Convert big.Int to fixed-length bytes with FillBytes, not Bytes().
- Dispatch on curve ID and use the matching ElementLen constant.
- Never pass hex/base64 strings where raw bytes are expected.
When it happens
Trigger: Passing a 32-byte value to a P-521 element (66 bytes), a 31-byte value to P-256 (32 bytes), a hex string instead of raw bytes, or a value with a leading zero stripped.
Common situations: Cross-curve mix-ups (P-256 value fed to P-384 routine), big.Int.Bytes() that dropped a leading zero, un-decoded hex/base64, or assuming one curve's ElementLen for all curves.
Related errors
- invalid scalar length
- invalid {{.P}} compressed point encoding
- invalid {{.P}} point encoding
- {{.P}} point not on curve
- {{.P}} point is the point at infinity
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/d13c40c094cb28dc.
Report an issue: GitHub.