fatedier/frp · error
read message length error: %w
Error message
read message length error: %w
What it means
Framing-layer error from vnet's ReadMessage: it could not read the 4-byte little-endian length prefix that precedes every message on a vnet control/data stream. The %w wraps the underlying error — almost always io.EOF or a connection reset — meaning the peer went away or the stream is corrupted mid-frame.
Source
Thrown at pkg/vnet/message.go:36
"encoding/binary"
"fmt"
"io"
)
// Maximum message size
const (
maxMessageSize = 1024 * 1024 // 1MB
)
// Format: [length(4 bytes)][data(length bytes)]
// ReadMessage reads a framed message from the reader
func ReadMessage(r io.Reader) ([]byte, error) {
// Read length (4 bytes)
var length uint32
err := binary.Read(r, binary.LittleEndian, &length)
if err != nil {
return nil, fmt.Errorf("read message length error: %w", err)
}
// Check length to prevent DoS
if length == 0 {
return nil, fmt.Errorf("message length is 0")
}
if length > maxMessageSize {
return nil, fmt.Errorf("message too large: %d > %d", length, maxMessageSize)
}
// Read message data
data := make([]byte, length)
_, err = io.ReadFull(r, data)
if err != nil {
return nil, fmt.Errorf("read message data error: %w", err)
}
return data, nilView on GitHub (pinned to 6c8a8d0a97)
Solutions
- Inspect the wrapped error: io.ErrUnexpectedEOF or reset points to network drop; plain io.EOF means clean peer close — just reconnect
- Reconnect the frpc vnet session (frp retries automatically; restart frpc if it does not)
- Keep the underlying connection alive (vnet rides frp's control connection — check frp keepalive/TCP settings) and check frps logs at the same timestamp
- If both sides log framing errors simultaneously, suspect version mismatch between frpc and frps — align versions
Example fix
// before
func readLoop(r io.Reader) {
for {
data, err := vnet.ReadMessage(r)
if err != nil { log.Printf("fatal: %v", err); os.Exit(1) }
}
}
// after
for {
data, err := vnet.ReadMessage(r)
if err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { return } // peer gone
log.Printf("read message: %v", err)
return
}
handle(data)
} Defensive patterns
Strategy: try-catch
Try / catch
data, err := vnet.ReadMessage(r)
if err != nil {
var wrErr error = err
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, net.ErrClosed) {
return // peer closed: reconnect at a higher level
}
return fmt.Errorf("vnet stream failed: %w", wrErr)
} Prevention
- Always check the wrapped error (%w) before deciding retry vs teardown
- Keep frpc/frps versions in lockstep to avoid framing drift
- Investigate simultaneous errors on both ends for the same connection
When it happens
Trigger: The remote end (frps or frpc vnet component) closes the connection while ReadMessage is blocked reading the header; a network interruption resets the TCP connection; reading from a stream where a previous partial read left the cursor misaligned (protocol desync).
Common situations: frps restart or crash while vnet is active; NAT/firewall idle timeout killing the virtual-net connection; one side upgraded to a version with a different framing.
Related errors
- unexpected frame type %d, want %d
- unsupported frame flags: %d
- parse route %s error: %v
- no route found for destination %s
- no route found for source %s
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/edf33d4b1ea4d114.
Report an issue: GitHub.