fatedier/frp · error
destinationIP is required
Error message
destinationIP is required
What it means
Returned by NewVirtualNetPlugin in the visitor plugin package when the virtual_net visitor options omit destinationIP. The plugin creates a TUN-backed route to a /32 (IPv4) or /128 (IPv6) host route derived from that IP, so it is mandatory at construction time.
Source
Thrown at pkg/plugin/visitor/virtual_net.go:62
consecutiveErrors int // Tracks consecutive connection errors for exponential backoff
ctx context.Context
cancel context.CancelFunc
}
func NewVirtualNetPlugin(pluginCtx PluginContext, options v1.VisitorPluginOptions) (Plugin, error) {
opts := options.(*v1.VirtualNetVisitorPluginOptions)
p := &VirtualNetPlugin{
pluginCtx: pluginCtx,
routes: make([]net.IPNet, 0),
}
p.ctx, p.cancel = context.WithCancel(pluginCtx.Ctx)
if opts.DestinationIP == "" {
return nil, errors.New("destinationIP is required")
}
// Parse DestinationIP and create a host route.
ip := net.ParseIP(opts.DestinationIP)
if ip == nil {
return nil, fmt.Errorf("invalid destination IP address [%s]", opts.DestinationIP)
}
var mask net.IPMask
if ip.To4() != nil {
mask = net.CIDRMask(32, 32) // /32 for IPv4
} else {
mask = net.CIDRMask(128, 128) // /128 for IPv6
}
p.routes = append(p.routes, net.IPNet{IP: ip, Mask: mask})
return p, nil
}View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Add destinationIP to the visitor block, e.g. destinationIP = "10.6.6.6" — the frpc TUN device routes to this address
- Ensure it is a valid single IP (no CIDR suffix); invalid IPs raise a separate 'invalid destination IP address' error
- Confirm server side has virtualNetConfig enabled and the visitor type is spelled virtual_net
Example fix
# before [[visitors]] name = "vt" type = "virtual_net" serverName = "p2p-server" secretKey = "s" bindAddr = "127.0.0.1" # after [[visitors]] name = "vt" type = "virtual_net" serverName = "p2p-server" secretKey = "s" bindAddr = "127.0.0.1" destinationIP = "10.6.6.6"
Defensive patterns
Strategy: validation
Validate before calling
for _, v := range visitorCfgs {
if v.GetType() == "virtual_net" {
vn := v.(*v1.VirtualNetVisitorPluginOptions) // or read via typed config
if vn.DestinationIP == "" {
return fmt.Errorf("visitor %s: virtual_net requires destinationIP", v.GetName())
}
if net.ParseIP(vn.DestinationIP) == nil {
return fmt.Errorf("visitor %s: invalid destinationIP %q", v.GetName(), vn.DestinationIP)
}
}
} Type guard
func validDestinationIP(ip string) bool {
return net.ParseIP(ip) != nil // single IP, no CIDR suffix
} Prevention
- frpc verify catches a missing destinationIP before the visitor is created
- Reserve a dedicated overlay subnet for virtual_net and pick destinationIPs from it deliberately
When it happens
Trigger: Declaring a [[visitors]] with type = "virtual_net" in frpc.toml without destinationIP; programmatic VisitorConfig for virtual_net with DestinationIP left empty string.
Common situations: Copying a virtual_net visitor example and editing only bindAddr/port; upgrading frp versions where the option was renamed or moved; enabling virtual net feature (server side virtualNetConfig) without completing the visitor config.
Related errors
- unknown visitor type: %s
- unmarshal VisitorConfig error: %v
- unmarshal visitor plugin error: %v
- unknown visitor plugin type: %s
- unmarshal VisitorPluginOptions error: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/1627b3b6fb404df5.
Report an issue: GitHub.