{"record":{"id":"6f9e597c1a67b06f","repo":"fatedier/frp","slug":"parse-route-s-error-v","errorCode":null,"errorMessage":"parse route %s error: %v","messagePattern":"parse route (.+?) error: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/vnet/controller.go","lineNumber":266,"sourceCode":"\n// UnregisterClientRoute Remove client route from routing table\nfunc (c *Controller) UnregisterClientRoute(name string) {\n\tc.clientRouter.delRoute(name)\n}\n\n// StartServerConnReadLoop starts the read loop for a server connection\n// (dynamically associates with source IPs)\nfunc (c *Controller) StartServerConnReadLoop(ctx context.Context, conn io.ReadWriteCloser, onClose func()) {\n\tgo c.readLoopServer(ctx, conn, onClose)\n}\n\n// ParseRoutes Convert route strings to IPNet objects\nfunc ParseRoutes(routeStrings []string) ([]net.IPNet, error) {\n\troutes := make([]net.IPNet, 0, len(routeStrings))\n\tfor _, r := range routeStrings {\n\t\t_, ipNet, err := net.ParseCIDR(r)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parse route %s error: %v\", r, err)\n\t\t}\n\t\troutes = append(routes, *ipNet)\n\t}\n\treturn routes, nil\n}\n\n// Client router (based on destination IP routing)\ntype clientRouter struct {\n\troutes map[string]*routeElement\n\tmu     sync.RWMutex\n}\n\nfunc newClientRouter() *clientRouter {\n\treturn &clientRouter{\n\t\troutes: make(map[string]*routeElement),\n\t}\n}\n","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/vnet/controller.go#L248-L284","documentation":"Thrown by vnet's ParseRoutes when converting the routes config ([]string of CIDRs) to net.IPNet: net.ParseCIDR failed for one entry. Each route string must be a valid CIDR network including the prefix length; the message names the offending entry and the parse error. This runs during vnet (virtual network) controller setup on both frpc and frps.","triggerScenarios":"vnet.enabled = true with routes like \"10.0.0.0\" (missing /24), \"10.0.0.1/8\" (host bits — ParseCIDR still accepts this, but truly malformed like \"10.0/8\" or \"abc\" fails), empty strings, or IPv6 typos. The routes array in [vnet] section of frpc.toml/frps.toml.","commonSituations":"Writing bare IPs instead of CIDRs in routes; trailing comma producing an empty entry; assuming a DNS name or port range is allowed in routes.","solutions":["Write every entry as base/prefix CIDR: routes = [\"10.0.0.0/24\", \"fd00::/64\"]","Remove empty entries (no trailing commas) and any hostnames — only literal CIDRs are accepted","Verify config (frpc verify / frps verify) before starting with vnet enabled"],"exampleFix":"# before\n[vnet]\nenabled = true\nroutes = [\"10.0.0.0\"]\n\n# after\n[vnet]\nenabled = true\nroutes = [\"10.0.0.0/24\"]","handlingStrategy":"validation","validationCode":"func validCIDRs(routes []string) bool {\n    for _, r := range routes {\n        if _, _, err := net.ParseCIDR(strings.TrimSpace(r)); err != nil { return false }\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Every vnet routes entry must be base/prefix CIDR, e.g. 10.0.0.0/24","No hostnames, no bare IPs, no empty strings","Run frpc/frps verify before enabling vnet in production"],"tags":["vnet","config","network","validation"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}