fatedier/frp · error
missing v2 crypto negotiation
Error message
missing v2 crypto negotiation
What it means
When a connection negotiates wire protocol v2, newControlReadWriter requires an AEAD crypto context established during negotiation (cryptoContext). If wireProtocol is v2 but cryptoContext is nil, the server rejects creating the control read-writer with this invariant error: v2's encryption layer was never set up for this connection.
Source
Thrown at server/service.go:610
return acceptedConn, nil
}
func writeWithDeadline(conn net.Conn, timeout time.Duration, writeFn func() error) error {
_ = conn.SetWriteDeadline(time.Now().Add(timeout))
defer func() {
_ = conn.SetWriteDeadline(time.Time{})
}()
return writeFn()
}
func (ac *acceptedConnection) messageConnFor(rw io.ReadWriter) *msg.Conn {
return msg.NewConn(ac.conn, msg.NewReadWriter(rw, ac.wireProtocol))
}
func (ac *acceptedConnection) newControlReadWriter(rw io.ReadWriter, key []byte) (io.ReadWriter, error) {
if ac.wireProtocol == wire.ProtocolV2 {
if ac.cryptoContext == nil {
return nil, fmt.Errorf("missing v2 crypto negotiation")
}
return netpkg.NewAEADCryptoReadWriter(
rw,
key,
netpkg.AEADCryptoRoleServer,
ac.cryptoContext.Algorithm,
ac.cryptoContext.TranscriptHash,
)
}
return netpkg.NewCryptoReadWriter(rw, key)
}
func (ac *acceptedConnection) readFirstV2Msg(conn net.Conn, wireConn *wire.Conn) (msg.Message, error) {
frame, err := wireConn.ReadFrame()
if err != nil {
return nil, fmt.Errorf("read v2 frame: %w", err)
}
if frame.Type == wire.FrameTypeClientHello {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Upgrade frpc and frps to the same version so the v2 negotiation flow matches on both ends
- Capture which side aborts the negotiation (server logs around the login) and inspect the wrapped handshake
- As a stopgap, configure the client to use the v1 wire protocol until versions align
Defensive patterns
Strategy: try-catch
Try / catch
rw, err := ac.newControlReadWriter(rw, key)
if err != nil && strings.Contains(err.Error(), "missing v2 crypto negotiation") {
// drop the connection; do not fall back to plaintext. Negotiation must be redone.
return err
} Prevention
- Keep frpc and frps versions locked together when using wire protocol v2
- Never construct v2 control connections without completing the crypto negotiation step
- In custom clients, treat cryptoContext == nil on a v2 connection as a hard precondition failure
When it happens
Trigger: A v2 client whose crypto negotiation (hello/auth exchange carrying algorithm and transcript hash) never completed before the control connection is finalized; version skew where one side enables v2 crypto but the other skips negotiation; internal code paths creating a control connection without running the negotiation step.
Common situations: Mixed frpc/frps versions during a rolling upgrade of the v2 protocol; middleboxes truncating the handshake; custom clients implementing v2 incompletely.
Related errors
- no supported crypto algorithm
- serverHello.Error
- create control crypto read writer: %w
- invalid crypto client random length %d, want %d
- unknown selected crypto algorithm: %s
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/baeed520930f6d37.
Report an issue: GitHub.