{"record":{"id":"421ef9452417899d","repo":"fatedier/frp","slug":"invalid-run-id-w","errorCode":null,"errorMessage":"invalid run id: %w","messagePattern":"invalid run id: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service.go","lineNumber":796,"sourceCode":"\t\t}\n\tcase wire.ProtocolV2:\n\t\tif udpPacketCodec != \"\" && udpPacketCodec != wire.UDPPacketCodecBinary {\n\t\t\treturn nil, fmt.Errorf(\"unsupported UDP packet codec selection: %s\", udpPacketCodec)\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported wire protocol: %s\", wireProtocol)\n\t}\n\t// If client's RunID is empty, it's a new client, we just create a new controller.\n\t// Otherwise, we check if there is one controller has the same run id. If so, we release previous controller and start new one.\n\tvar err error\n\tif loginMsg.RunID == \"\" {\n\t\tloginMsg.RunID, err = util.RandID()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif err := validation.ValidateRunID(loginMsg.RunID); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid run id: %w\", err)\n\t}\n\n\tctx := netpkg.NewContextFromConn(ctlConn)\n\txl := xlog.FromContextSafe(ctx)\n\txl.AppendPrefix(loginMsg.RunID)\n\tctx = xlog.NewContext(ctx, xl)\n\txl.Infof(\"client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]\",\n\t\tctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)\n\n\t// Check auth.\n\tauthVerifier := svr.auth.Verifier\n\tif internal && loginMsg.ClientSpec.AlwaysAuthPass {\n\t\tauthVerifier = auth.AlwaysPassVerifier\n\t}\n\tif err := authVerifier.VerifyLogin(loginMsg); err != nil {\n\t\treturn nil, err\n\t}\n","sourceCodeStart":778,"sourceCodeEnd":814,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/server/service.go#L778-L814","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlogin := &msg.Login{RunID: fmt.Sprintf(\"%s-%s-%s\", host, user, tags)}\n\n// after\nlogin := &msg.Login{RunID: \"\"} // server assigns a valid random run id","handlingStrategy":"validation","validationCode":"import \"github.com/fatedier/frp/pkg/config/v1/validation\"\n\n// before login\nif err := validation.ValidateRunID(runID); err != nil {\n    // regenerate or trim the run id instead of sending it\n    runID = \"\"\n}\nlogin := &msg.Login{RunID: runID}","typeGuard":"func isValidRunID(s string) bool {\n    return s != \"\" && len(s) <= 64 && utf8.ValidString(s)\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["frp","run-id","validation","login"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}