fatedier/frp · critical

create control crypto read writer: %w

Error message

create control crypto read writer: %w

What it means

During the frpc-frps control connection login, after authentication succeeds the client upgrades the raw TCP conn to an encrypted read/writer via newControlReadWriter using the negotiated crypto context. This error wraps any failure of that crypto setup, whose real causes live in pkg/proto/wire: un-JSON-able hello transcripts, an unknown AEAD algorithm selected by the server, an algorithm the client never advertised, or a wrong-length server random.

Source

Thrown at client/control_session.go:90

	loginMsg, err := d.buildLoginMsg(previousRunID)
	if err != nil {
		return nil, err
	}

	loginResult, err := d.exchangeLogin(conn, loginMsg)
	if err != nil {
		return nil, err
	}
	loginRespMsg := loginResult.resp
	if loginRespMsg.Error != "" {
		return nil, errors.New(loginRespMsg.Error)
	}

	var controlRW io.ReadWriter = conn
	if d.clientSpec == nil || d.clientSpec.Type != "ssh-tunnel" {
		controlRW, err = d.newControlReadWriter(conn, loginResult.crypto)
		if err != nil {
			return nil, fmt.Errorf("create control crypto read writer: %w", err)
		}
	}

	success = true
	return &SessionContext{
		Common:         d.common,
		RunID:          loginRespMsg.RunID,
		Conn:           msg.NewConn(conn, msg.NewReadWriter(controlRW, d.common.Transport.WireProtocol)),
		Auth:           d.auth,
		Connector:      newMessageConnector(connector, d.common.Transport.WireProtocol),
		VnetController: d.vnetController,
		UDPPacketCodec: loginResult.udpPacketCodec,
	}, nil
}

func (d *controlSessionDialer) buildLoginMsg(previousRunID string) (*msg.Login, error) {
	hostname, _ := os.Hostname()
	loginMsg := &msg.Login{

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Run matching frpc and frps versions (same release)
  2. Ensure nothing between client and server terminates or rewrites the control connection (no TLS-offloading proxies on the frps port)
  3. Capture the wrapped cause — 'unknown selected crypto algorithm' vs 'decode ... transcript' points to server build vs payload corruption
  4. Retry on a clean network path to rule out middlebox interference
Defensive patterns

Strategy: try-catch

Try / catch

_, err := svc.Login(ctx)
if err != nil && strings.Contains(err.Error(), "create control crypto read writer") {
	// protocol/crypto mismatch with server: check version parity and network path
}

Prevention

When it happens

Trigger: Login to a server whose ServerHello selects a crypto algorithm the client did not offer, or that sends malformed hello payloads; effectively only with a mismatched/modified frps build or a man-in-the-middle altering the handshake. Skipped entirely for ssh-tunnel client spec connections.

Common situations: frpc and frps built from different forks/versions with divergent crypto negotiation; a transparent proxy (corporate firewall, TLS-terminating LB) corrupting the binary handshake; extremely rare protocol-level incompatibility after a downgrade.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/56207b507cff796c. Report an issue: GitHub.