fatedier/frp · error

create encryption stream error: %v

Error message

create encryption stream error: %v

What it means

wrapVisitorConn failed while turning the raw visitor connection into an encrypted stream via libio.WithEncryption, which initializes an AES cipher by exchanging a salt and derived key over the connection. Failure here is almost always an I/O error writing the initialization data, because AES key derivation itself does not error for arbitrary secrets.

Source

Thrown at client/visitor/visitor.go:209

		visitorConn.Close()
		return nil, fmt.Errorf("read newVisitorConnRespMsg error: %v", err)
	}
	_ = visitorConn.SetReadDeadline(time.Time{})

	if newVisitorConnRespMsg.Error != "" {
		visitorConn.Close()
		return nil, fmt.Errorf("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
	}
	return visitorConn, nil
}

func wrapVisitorConn(conn io.ReadWriteCloser, cfg *v1.VisitorBaseConfig) (io.ReadWriteCloser, func(), error) {
	rwc := conn
	if cfg.Transport.UseEncryption {
		var err error
		rwc, err = libio.WithEncryption(rwc, []byte(cfg.SecretKey))
		if err != nil {
			return nil, func() {}, fmt.Errorf("create encryption stream error: %v", err)
		}
	}
	recycleFn := func() {}
	if cfg.Transport.UseCompression {
		rwc, recycleFn = libio.WithCompressionFromPool(rwc)
	}
	return rwc, recycleFn, nil
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Ensure useEncryption matches on both sides: the visitor config and the stcp/xtcp proxy config must both set transport.useEncryption = true.
  2. Check frps and the serving frpc logs for the peer closing the stream right at handshake time.
  3. If this appears together with errors 120/121 intermittently, the same flaky network path is the root cause.
  4. Retry the visitor connection; persistent recurrence points to the encryption flag mismatch.
Defensive patterns

Strategy: validation

Validate before calling

// Ensure both endpoints agree on encryption before dialing
vBase := visitorCfg.GetBaseConfig()
pBase := proxyCfg.GetBaseConfig()
if vBase.Transport.UseEncryption != pBase.Transport.UseEncryption {
    return fmt.Errorf("useEncryption mismatch between visitor and proxy")
}

Prevention

When it happens

Trigger: libio.WithEncryption(rwc, []byte(cfg.SecretKey)) returns an error when writing the key-exchange preamble to the just-established connection fails; the peer closed between NewVisitorConnResp and this step.

Common situations: Server dropped the connection immediately after a successful visitor handshake; TLS/middlebox truncating the stream; transport.useEncryption enabled on the visitor while the target proxy owner has it disabled, so the peer closes on unexpected ciphertext framing.

Related errors


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