fatedier/frp · error
zone is not valid UTF-8
Error message
zone is not valid UTF-8
What it means
The v2 binary UDP address decoder validates the zone bytes with utf8.Valid before converting them into net.UDPAddr.Zone. IPv6 zone identifiers are conventionally interface names, and Go's net package expects a valid string, so invalid UTF-8 is rejected as malformed input rather than propagated. This guards downstream string handling from binary garbage.
Source
Thrown at pkg/msg/udp_binary.go:215
}
if len(body)-offset < ipLen+3 {
return nil, offset, fmt.Errorf("truncated address")
}
ip := append(net.IP(nil), body[offset:offset+ipLen]...)
offset += ipLen
port := binary.BigEndian.Uint16(body[offset : offset+2])
offset += 2
zoneLen := int(body[offset])
offset++
if len(body)-offset < zoneLen {
return nil, offset, fmt.Errorf("truncated zone")
}
zoneBytes := body[offset : offset+zoneLen]
if family == 4 && zoneLen != 0 {
return nil, offset, fmt.Errorf("IPv4 zone is forbidden")
}
if !utf8.Valid(zoneBytes) {
return nil, offset, fmt.Errorf("zone is not valid UTF-8")
}
offset += zoneLen
return &net.UDPAddr{IP: ip, Port: int(port), Zone: string(zoneBytes)}, offset, nil
}
type V2BinaryUDPPacketReadWriter struct {
conn *wire.Conn
}
func NewV2BinaryUDPPacketReadWriter(rw io.ReadWriter) *V2BinaryUDPPacketReadWriter {
return &V2BinaryUDPPacketReadWriter{conn: wire.NewConn(rw)}
}
func (rw *V2BinaryUDPPacketReadWriter) ReadMsg() (Message, error) {
frame, err := rw.conn.ReadFrame()
if err != nil {
return nil, err
}View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Close and re-establish the connection — the frame stream is likely desynchronized or hostile.
- Use the library's EncodeUDPPacketBinary for sending so zones are always valid UTF-8.
- Verify both peers run matching frp versions.
- When fuzzing, treat this as a handled rejection.
Defensive patterns
Strategy: try-catch
Try / catch
if _, err := rw.ReadMsg(); err != nil {
if strings.Contains(err.Error(), "not valid UTF-8") {
conn.Close() // garbage or desynchronized stream
}
} Prevention
- Only send zones that are plain interface names.
- Close the connection on decode validation failures; do not attempt resync.
When it happens
Trigger: DecodeUDPPacketBinary receives an address whose zone bytes fail utf8.Valid — fuzzed input, a desynchronized stream being decoded as an address, or a custom encoder stuffing raw bytes into the zone field.
Common situations: Stream desynchronization after an earlier truncation error (error 264) makes random bytes land in the zone field; fuzzing corpora; hostile peers sending garbage frames.
Related errors
- IPv4 zone is forbidden
- truncated zone
- message frame payload too short
- invalid protocol
- %s must be valid UTF-8
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/fa0b52090ae9d88b.
Report an issue: GitHub.