fatedier/frp · error
nat_hole_stun_server can not be empty
Error message
nat_hole_stun_server can not be empty
What it means
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.
Source
Thrown at cmd/frpc/sub/nathole.go:97
natFeature, err := nathole.ClassifyNATFeature(addrs, localIPs)
if err != nil {
fmt.Println("classify nat feature error:", err)
os.Exit(1)
}
fmt.Println("STUN server:", cfg.NatHoleSTUNServer)
fmt.Println("Your NAT type is:", natFeature.NatType)
fmt.Println("Behavior is:", natFeature.Behavior)
fmt.Println("External address is:", addrs)
fmt.Println("Local address is:", localAddr.String())
fmt.Println("Public Network:", natFeature.PublicNetwork)
return nil
},
}
func validateForNatHoleDiscovery(cfg *v1.ClientCommonConfig) error {
if cfg.NatHoleSTUNServer == "" {
return fmt.Errorf("nat_hole_stun_server can not be empty")
}
return nil
}
View on GitHub (pinned to 6c8a8d0a97)
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.
Example fix
# before (frpc.toml) [common] serverAddr = "x" # after [common] serverAddr = "x" natHoleSTUNServer = "stun.easyvoip.com:3478"
Defensive patterns
Strategy: validation
Validate before calling
if cfg.NatHoleSTUNServer == "" {
return fmt.Errorf("set natHoleSTUNServer before nathole discovery")
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- not enough addresses
- classify client nat feature error: %v
- classify visitor nat feature error: %v
- wait response from stun server timeout
- no external address found
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/2feb461970a9cdd9.
Report an issue: GitHub.