fatedier/frp · error

create encryption stream error: %w

Error message

create encryption stream error: %w

What it means

Thrown when BaseProxy.wrapWorkConn fails to build the encrypted stream (libio.WithEncryption) over a work connection after frps assigns a user connection. WithEncryption initializes an AES-CFB writer/reader from the shared key derived during login; failure almost always means the key material is invalid (empty or malformed), not a network problem. The underlying error is wrapped with %w.

Source

Thrown at client/proxy/proxy.go:146

// wrapWorkConn applies rate limiting, encryption, and compression
// to a work connection based on the proxy's transport configuration.
// The returned recycle function should be called when the stream is no longer in use
// to return compression resources to the pool. It is safe to not call recycle,
// in which case resources will be garbage collected normally.
func (pxy *BaseProxy) wrapWorkConn(conn net.Conn, encKey []byte) (io.ReadWriteCloser, func(), error) {
	var rwc io.ReadWriteCloser = conn
	if pxy.limiter != nil {
		rwc = libio.WrapReadWriteCloser(limit.NewReader(conn, pxy.limiter), limit.NewWriter(conn, pxy.limiter), func() error {
			return conn.Close()
		})
	}
	if pxy.baseCfg.Transport.UseEncryption {
		var err error
		rwc, err = libio.WithEncryption(rwc, encKey)
		if err != nil {
			conn.Close()
			return nil, nil, fmt.Errorf("create encryption stream error: %w", err)
		}
	}
	var recycleFn func()
	if pxy.baseCfg.Transport.UseCompression {
		rwc, recycleFn = libio.WithCompressionFromPool(rwc)
	}
	return rwc, recycleFn, nil
}

func (pxy *BaseProxy) SetInWorkConnCallback(cb func(*v1.ProxyBaseConfig, net.Conn, *msg.StartWorkConn) bool) {
	pxy.inWorkConnCallback = cb
}

func (pxy *BaseProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
	if pxy.inWorkConnCallback != nil {
		if !pxy.inWorkConnCallback(pxy.baseCfg, conn, m) {
			return
		}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Ensure frpc and frps share the same auth configuration (same auth.token, same auth.method) so both sides derive the same key.
  2. Upgrade frpc and frps to matching versions.
  3. As a diagnostic, temporarily disable transport.useEncryption to confirm the rest of the path works, then re-enable once auth is aligned.
  4. Inspect the wrapped error (%w) — an AEAD/key-size error points at bad key derivation, a network error at the conn itself.

Example fix

# before (frpc.toml)
auth.token = ""
[proxies.transport]
useEncryption = true

# after
auth.token = "shared-secret"
[proxies.transport]
useEncryption = true
Defensive patterns

Strategy: try-catch

Try / catch

// In custom proxies reusing wrapWorkConn: on encryption-stream error, close conn and surface wrapped cause
rwc, recycle, err := pxy.wrapWorkConn(conn, encKey)
if err != nil {
    var encErr error
    if errors.As(fmt.Unwrap(err), &encErr) || strings.Contains(err.Error(), "encryption stream") {
        log.Errorf("encryption setup failed (auth/token mismatch?): %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: A proxy with transport.useEncryption = true where the client's encKey (derived from the auth token/secret between frpc and frps) is empty or invalid when the work conn is wrapped — typically a key-derivation/mismatch problem rather than transient I/O.

Common situations: auth.token mismatch or empty token combined with custom auth hooks producing empty keys; version skew between frpc and frps changing key derivation; only encryption enabled without a usable shared secret.

Related errors


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