{"record":{"id":"2feb461970a9cdd9","repo":"fatedier/frp","slug":"nat-hole-stun-server-can-not-be-empty","errorCode":null,"errorMessage":"nat_hole_stun_server can not be empty","messagePattern":"nat_hole_stun_server can not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/frpc/sub/nathole.go","lineNumber":97,"sourceCode":"\n\t\tnatFeature, err := nathole.ClassifyNATFeature(addrs, localIPs)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"classify nat feature error:\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfmt.Println(\"STUN server:\", cfg.NatHoleSTUNServer)\n\t\tfmt.Println(\"Your NAT type is:\", natFeature.NatType)\n\t\tfmt.Println(\"Behavior is:\", natFeature.Behavior)\n\t\tfmt.Println(\"External address is:\", addrs)\n\t\tfmt.Println(\"Local address is:\", localAddr.String())\n\t\tfmt.Println(\"Public Network:\", natFeature.PublicNetwork)\n\t\treturn nil\n\t},\n}\n\nfunc validateForNatHoleDiscovery(cfg *v1.ClientCommonConfig) error {\n\tif cfg.NatHoleSTUNServer == \"\" {\n\t\treturn fmt.Errorf(\"nat_hole_stun_server can not be empty\")\n\t}\n\treturn nil\n}\n","sourceCodeStart":79,"sourceCodeEnd":101,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/cmd/frpc/sub/nathole.go#L79-L101","documentation":"The `frpc nathole` discovery command refused to run because natHoleSTUNServer is empty in the client common config. NAT-hole discovery fundamentally needs a STUN server to learn external addresses and NAT behavior, so the command fails fast before any network activity.","triggerScenarios":"Running `frpc nathole discover` (or the nathole command with only a cfgFile arg) while the loaded ClientCommonConfig has no natHoleSTUNServer set and no default applies.","commonSituations":"Config file omits natHoleSTUNServer and the user expected a default; a minimal or custom TOML/YAML that skipped the common section; older configs where the field was named differently during migration to the v1 schema.","solutions":["Set natHoleSTUNServer in the common config, e.g. 'stun.easyvoip.com:3478' or another reachable STUN endpoint.","If it should already be set, verify the config file actually loads without parse errors and the key sits where your frp version expects it.","Pass a config file to the nathole command; running without cfgFile leaves everything default."],"exampleFix":"# before (frpc.toml)\n[common]\nserverAddr = \"x\"\n\n# after\n[common]\nserverAddr = \"x\"\nnatHoleSTUNServer = \"stun.easyvoip.com:3478\"","handlingStrategy":"validation","validationCode":"if cfg.NatHoleSTUNServer == \"\" {\n    return fmt.Errorf(\"set natHoleSTUNServer before nathole discovery\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include natHoleSTUNServer in configs intended for xtcp","Template the STUN server into generated configs","Test the command in CI with a fully populated common section"],"tags":["config","nathole","stun","cli"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}