fatedier/frp · error
invalid run id: %w
Error message
invalid run id: %w
What it means
Returned by Service.RegisterControl when validation.ValidateRunID rejects the client-supplied Login.RunID (or the server-generated one). ValidateRunID (pkg/config/v1/validation/name.go) requires the run id to be non-empty, at most 64 bytes, valid UTF-8, and contain only printable characters. The run id keys the control connection in ctlManager, so malformed ids are refused at login time.
Source
Thrown at server/service.go:796
}
case wire.ProtocolV2:
if udpPacketCodec != "" && udpPacketCodec != wire.UDPPacketCodecBinary {
return nil, fmt.Errorf("unsupported UDP packet codec selection: %s", udpPacketCodec)
}
default:
return nil, fmt.Errorf("unsupported wire protocol: %s", wireProtocol)
}
// If client's RunID is empty, it's a new client, we just create a new controller.
// Otherwise, we check if there is one controller has the same run id. If so, we release previous controller and start new one.
var err error
if loginMsg.RunID == "" {
loginMsg.RunID, err = util.RandID()
if err != nil {
return nil, err
}
}
if err := validation.ValidateRunID(loginMsg.RunID); err != nil {
return nil, fmt.Errorf("invalid run id: %w", err)
}
ctx := netpkg.NewContextFromConn(ctlConn)
xl := xlog.FromContextSafe(ctx)
xl.AppendPrefix(loginMsg.RunID)
ctx = xlog.NewContext(ctx, xl)
xl.Infof("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]",
ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)
// Check auth.
authVerifier := svr.auth.Verifier
if internal && loginMsg.ClientSpec.AlwaysAuthPass {
authVerifier = auth.AlwaysPassVerifier
}
if err := authVerifier.VerifyLogin(loginMsg); err != nil {
return nil, err
}
View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Let frp generate the run id: send Login.RunID empty so the server assigns util.RandID()
- If you set RunID yourself, keep it under 64 bytes, valid UTF-8, printable ASCII-safe (e.g. UUID/hex)
- Upgrade both frpc and frps so run id generation follows the same rules
- Inspect the login payload on the wire to confirm the run id is not being truncated or corrupted
Example fix
// before
login := &msg.Login{RunID: fmt.Sprintf("%s-%s-%s", host, user, tags)}
// after
login := &msg.Login{RunID: ""} // server assigns a valid random run id Defensive patterns
Strategy: validation
Validate before calling
import "github.com/fatedier/frp/pkg/config/v1/validation"
// before login
if err := validation.ValidateRunID(runID); err != nil {
// regenerate or trim the run id instead of sending it
runID = ""
}
login := &msg.Login{RunID: runID} Type guard
func isValidRunID(s string) bool {
return s != "" && len(s) <= 64 && utf8.ValidString(s)
} Prevention
- Leave RunID empty and let the server generate it
- If you must set it, derive it from UUID/hex output — never raw hostnames or free text
- Add a unit test asserting your run id generator stays within ValidateRunID rules
When it happens
Trigger: A Login message whose RunID exceeds 64 bytes, is empty after generation failure, contains non-UTF-8 bytes, or includes control/non-printable characters. Seen when a custom or patched client injects its own run id, or when a middlebox corrupts the login message.
Common situations: Custom clients generating run ids from hostnames/user input that are too long or contain unusual characters; older frpc forks that set run id from an unvalidated field; fuzzed or corrupted login frames; proxies that alter the JSON/yaml login payload.
Related errors
- invalid pool count %d, must be non-negative
- exec configuration is required when type is 'exec'
- file path cannot be empty
- exec command cannot be empty
- exec env name cannot be empty
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/421ef9452417899d.
Report an issue: GitHub.