{"record":{"id":"181b8d8b7261d4d9","repo":"fatedier/frp","slug":"invalid-destination-ip-address-s","errorCode":null,"errorMessage":"invalid destination IP address [%s]","messagePattern":"invalid destination IP address \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/plugin/visitor/virtual_net.go","lineNumber":68,"sourceCode":"\nfunc NewVirtualNetPlugin(pluginCtx PluginContext, options v1.VisitorPluginOptions) (Plugin, error) {\n\topts := options.(*v1.VirtualNetVisitorPluginOptions)\n\n\tp := &VirtualNetPlugin{\n\t\tpluginCtx: pluginCtx,\n\t\troutes:    make([]net.IPNet, 0),\n\t}\n\n\tp.ctx, p.cancel = context.WithCancel(pluginCtx.Ctx)\n\n\tif opts.DestinationIP == \"\" {\n\t\treturn nil, errors.New(\"destinationIP is required\")\n\t}\n\n\t// Parse DestinationIP and create a host route.\n\tip := net.ParseIP(opts.DestinationIP)\n\tif ip == nil {\n\t\treturn nil, fmt.Errorf(\"invalid destination IP address [%s]\", opts.DestinationIP)\n\t}\n\n\tvar mask net.IPMask\n\tif ip.To4() != nil {\n\t\tmask = net.CIDRMask(32, 32) // /32 for IPv4\n\t} else {\n\t\tmask = net.CIDRMask(128, 128) // /128 for IPv6\n\t}\n\tp.routes = append(p.routes, net.IPNet{IP: ip, Mask: mask})\n\n\treturn p, nil\n}\n\nfunc (p *VirtualNetPlugin) Name() string {\n\treturn v1.VisitorPluginVirtualNet\n}\n\nfunc (p *VirtualNetPlugin) Start() {","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/plugin/visitor/virtual_net.go#L50-L86","documentation":"The virtual_net visitor plugin could not parse its DestinationIP option: net.ParseIP returned nil for the configured string. DestinationIP must be a literal IPv4 or IPv6 address (host route, /32 or /128), not a CIDR, hostname, or empty string (empty is caught earlier by a dedicated error).","triggerScenarios":"Creating a virtual_net visitor plugin with opts.DestinationIP that net.ParseIP rejects — e.g. \"10.0.0.0/24\", \"myhost\", \"10.0.0\" (malformed octets), or trailing whitespace.","commonSituations":"User expects CIDR support and puts a network prefix in destinationIP; hostname given instead of an IP; copy-paste artifact like quotes or spaces in the TOML value.","solutions":["Set destinationIP to a single literal IP address, e.g. 10.0.0.1 or fd00::1","Remove any CIDR suffix, hostname, quotes, or surrounding whitespace","One plugin instance per destination host; add more visitor entries for additional IPs"],"exampleFix":"// before (frpc.toml)\n[[visitors]]\nname = \"vn\"\ntype = \"stty\"\n[visitors.plugin]\ntype = \"virtual_net\"\ndestinationIP = \"10.0.0.0/24\"\n\n// after\ndestinationIP = \"10.0.0.3\"","handlingStrategy":"validation","validationCode":"if net.ParseIP(cfg.DestinationIP) == nil {\n    return fmt.Errorf(\"destinationIP must be a literal IP, got %q\", cfg.DestinationIP)\n}","typeGuard":"func isValidLiteralIP(s string) bool {\n    return net.ParseIP(strings.TrimSpace(s)) != nil && !strings.Contains(s, \"/\")\n}","tryCatchPattern":null,"preventionTips":["Remember destinationIP takes one host IP, not a CIDR — use multiple visitors for multiple hosts","Validate IP strings in config linting before deployment"],"tags":["plugin","configuration","ip-parsing","visitor","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}