fatedier/frp · error
VirtualNet feature is not enabled; enable it by setting the…
Error message
VirtualNet feature is not enabled; enable it by setting the appropriate feature gate flag
What it means
frp's client config validator rejects a configuration that sets virtualNet.address but does not enable the VirtualNet feature gate. Feature gates are an opt-in mechanism for experimental features, so the virtual network configuration is only honored when the gate 'VirtualNet' is explicitly set. The check runs during ValidateClientCommonConfig before the client starts.
Solutions
- Add featureGates = { VirtualNet = true } (TOML: [featureGates]\nVirtualNet = true) to the client config
- Verify the gate key casing matches featuregate.VirtualNet exactly (case-sensitive)
- If you do not need the virtual network, remove the [virtualNet] section (address field) from the config
- Check frp release notes: VirtualNet is experimental and may require a build/version that includes it
Example fix
# before [featureGates] # (missing) [virtualNet] address = "10.26.0.1/16" # after [featureGates] VirtualNet = true [virtualNet] address = "10.26.0.1/16"
Defensive patterns
Strategy: validation
Validate before calling
// Before starting frpc, validate the pair in Go:
func hasVirtualNetGate(c *v1.ClientCommonConfig) bool {
gates := featuregate.NewFeatureGate()
if err := gates.SetFromMap(c.FeatureGates); err != nil {
return false
}
return gates.Enabled(featuregate.VirtualNet) || c.VirtualNet.Address == ""
} Try / catch
if _, err := validation.ValidateClientCommonConfig(cfg); err != nil {
if strings.Contains(err.Error(), "VirtualNet feature is not enabled") {
// set FeatureGates["VirtualNet"]=true or clear VirtualNet.Address, then retry validation
}
return err
} Prevention
- Keep featureGates and the feature sections adjacent in config templates
- Add a CI step running `frpc verify -c` on every config change
- Document gate names case-sensitively
When it happens
Trigger: ClientCommonConfig has FeatureGates = nil or lacks key "VirtualNet", while c.VirtualNet.Address is non-empty. Happens when loading a TOML/YAML/JSON client config with a [virtualNet] section but no [featureGates] VirtualNet = true entry, or when gates.SetFromMap fails to see the key.
Common situations: Copying a virtualNet config block from docs or another machine without the featureGates block; upgrading frp where VirtualNet moved behind a gate; typos in the gate key (e.g. 'virtualnet' lowercase) so the gate map parses but never matches featuregate.VirtualNet.
Related errors
- apply config failed
- cannot specify both auth.oidc.tokenSource and any other…
- cannot specify both auth.token and auth.tokenSource
- decode proxy at index
- decode visitor at index
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/529391ad9d12615a.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/config/v1/validation/client.go:61
}
for _, validator := range validators {
w, err := validator()
warnings = AppendError(warnings, w)
errs = AppendError(errs, err)
}
return warnings, errs
}
func validateFeatureGates(c *v1.ClientCommonConfig) (Warning, error) {
gates := featuregate.NewFeatureGate()
if err := gates.SetFromMap(c.FeatureGates); err != nil {
return nil, err
}
if c.VirtualNet.Address != "" {
if !gates.Enabled(featuregate.VirtualNet) {
return nil, fmt.Errorf("VirtualNet feature is not enabled; enable it by setting the appropriate feature gate flag")
}
}
return nil, nil
}
// ClientConfigRequirements describes runtime capabilities needed by a client configuration.
type ClientConfigRequirements struct {
VirtualNet bool
}
// GetClientConfigRequirements returns the runtime capabilities needed by a client configuration.
func GetClientConfigRequirements(
common *v1.ClientCommonConfig,
proxyCfgs []v1.ProxyConfigurer,
visitorCfgs []v1.VisitorConfigurer,
) ClientConfigRequirements {
requirements := ClientConfigRequirements{}
if common != nil && common.VirtualNet.Address != "" {View on GitHub (pinned to 6c8a8d0a97)