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
- Ensure frpc and frps share the same auth configuration (same auth.token, same auth.method) so both sides derive the same key.
- Upgrade frpc and frps to matching versions.
- As a diagnostic, temporarily disable transport.useEncryption to confirm the rest of the path works, then re-enable once auth is aligned.
- 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
- Keep auth.token and auth.method identical on frpc and frps when useEncryption is on.
- Run same major/minor versions of frpc and frps.
- Test with encryption disabled first to isolate key-derivation issues.
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
- login to the server failed: %v. With loginFailExit enabled,
- HTTP ${response.status}
- ErrInvalidArgument
- ErrNotFound
- ErrConflict
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/65dc2efd0ad269b9.
Report an issue: GitHub.