{"record":{"id":"822ef7aabbcbdd68","repo":"fatedier/frp","slug":"unsupported-wire-protocol-s","errorCode":null,"errorMessage":"unsupported wire protocol: %s","messagePattern":"unsupported wire protocol: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service.go","lineNumber":784,"sourceCode":"\nfunc (svr *Service) RegisterControl(\n\tctlConn *msg.Conn,\n\tloginMsg *msg.Login,\n\tinternal bool,\n\twireProtocol string,\n\tudpPacketCodec string,\n) (*Control, error) {\n\tswitch wireProtocol {\n\tcase wire.ProtocolV1:\n\t\tif udpPacketCodec != \"\" {\n\t\t\treturn nil, fmt.Errorf(\"UDP packet codec %q requires wire protocol v2\", udpPacketCodec)\n\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)","sourceCodeStart":766,"sourceCodeEnd":802,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/server/service.go#L766-L802","documentation":"Returned by Service.RegisterControl when the wire protocol string carried on the client's control connection is neither wire.ProtocolV1 nor wire.ProtocolV2. The server switches on the negotiated wire protocol before it even creates a controller, so an unknown value aborts the login immediately. It exists to reject clients that send a protocol version this frps build does not understand.","triggerScenarios":"Calling RegisterControl (or logging in from an frpc) with a wireProtocol argument other than the exact strings \"v1\"/\"v2\" (e.g. \"v3\", \"\", \"V2\" with wrong casing). Typically happens when a custom client hardcodes a protocol name, when a ClientHello negotiates an unknown value, or when mixing a newer frpc with an older frps that lacks that protocol constant.","commonSituations":"Version skew between frpc and frps (client newer than server and advertising a protocol the server predates); hand-rolled clients or tests that pass an arbitrary protocol string; typo in transport.wireProtocol config; proxy/load balancer mangling the handshake.","solutions":["Align frpc and frps versions so both support the same wire protocol set","Set transport.wireProtocol to an empty string (defaults to v1) or exactly \"v1\"/\"v2\" in the client config","If you drive RegisterControl programmatically, pass only wire.ProtocolV1 or wire.ProtocolV2 constants instead of string literals","Check server logs for the exact offending value in the message to identify which peer sent it"],"exampleFix":"# frpc.toml — before\ntransport.wireProtocol = \"v3\"\n\n# after\ntransport.wireProtocol = \"v2\"","handlingStrategy":"validation","validationCode":"import \"github.com/fatedier/frp/pkg/wire\"\n\nfunc validWireProtocol(p string) bool {\n    return p == \"\" || p == wire.ProtocolV1 || p == wire.ProtocolV2\n}\n// client side: if !validWireProtocol(cfg.Transport.WireProtocol) { fix config before dialing }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pin frpc and frps to compatible versions so the wire protocol vocabulary matches","Never hand-type protocol strings; import the wire package constants","Fail fast on config load if wireProtocol is not one of the known constants"],"tags":["frp","wire-protocol","handshake","version-skew"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}