{"record":{"id":"2df393edce8762c6","repo":"fatedier/frp","slug":"visitor-name-s-is-duplicated","errorCode":null,"errorMessage":"visitor name [%s] is duplicated","messagePattern":"visitor name \\[(.+?)\\] is duplicated","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/load.go","lineNumber":441,"sourceCode":"\n// validateNoDuplicateNames rejects proxies or visitors that share a name. They are\n// keyed by name in the config sources, so a duplicate would otherwise be silently\n// overwritten and never started, with no error or log.\nfunc validateNoDuplicateNames(proxies []v1.ProxyConfigurer, visitors []v1.VisitorConfigurer) error {\n\tproxyNames := make(map[string]struct{}, len(proxies))\n\tfor _, p := range proxies {\n\t\tname := p.GetBaseConfig().Name\n\t\tif _, ok := proxyNames[name]; ok {\n\t\t\treturn fmt.Errorf(\"proxy name [%s] is duplicated\", name)\n\t\t}\n\t\tproxyNames[name] = struct{}{}\n\t}\n\n\tvisitorNames := make(map[string]struct{}, len(visitors))\n\tfor _, v := range visitors {\n\t\tname := v.GetBaseConfig().Name\n\t\tif _, ok := visitorNames[name]; ok {\n\t\t\treturn fmt.Errorf(\"visitor name [%s] is duplicated\", name)\n\t\t}\n\t\tvisitorNames[name] = struct{}{}\n\t}\n\n\treturn nil\n}\n\nfunc CompleteProxyConfigurers(proxies []v1.ProxyConfigurer) []v1.ProxyConfigurer {\n\tproxyCfgs := proxies\n\tfor _, c := range proxyCfgs {\n\t\tc.Complete()\n\t}\n\treturn proxyCfgs\n}\n\nfunc CompleteVisitorConfigurers(visitors []v1.VisitorConfigurer) []v1.VisitorConfigurer {\n\tvisitorCfgs := visitors\n\tfor _, c := range visitorCfgs {","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/load.go#L423-L459","documentation":"The visitor counterpart of the duplicate-name check: after aggregation, two visitors sharing one name make 'visitor name [<name>] is duplicated' fail the load, preventing the map-keyed silent overwrite that would otherwise drop one visitor.","triggerScenarios":"Two [[visitors]] entries in frpc.toml sharing one name, or the same visitor defined in the main file and again in an included file — includes are merged into one list before this check runs.","commonSituations":"Copy-pasted [[visitors]] blocks without renaming; visitors for the same serverName defined in both the main config and an include file; multiple operators adding visitors for the same secret service.","solutions":["Rename one of the visitor entries sharing the name","Search main config and all includes for the duplicated visitor name and keep a single definition","Use a naming convention (host-service-visitor) to avoid collisions across include files"],"exampleFix":"# before\n[[visitors]]\nname = \"stcp-visitor\"\n[[visitors]]\nname = \"stcp-visitor\"\n\n# after\n[[visitors]]\nname = \"stcp-visitor-a\"\n[[visitors]]\nname = \"stcp-visitor-b\"","handlingStrategy":"validation","validationCode":"names := sets.New[string]()\nfor _, v := range allVisitors { if names.Has(v.GetBaseConfig().Name) { return fmt.Errorf(\"duplicate visitor %s\", v.GetBaseConfig().Name) }; names.Insert(v.GetBaseConfig().Name) }","typeGuard":null,"tryCatchPattern":"if err := config.LoadClientConfig(path, strict); err != nil { if strings.Contains(err.Error(), \"visitor name\") && strings.Contains(err.Error(), \"duplicated\") { /* rename and reload */ } }","preventionTips":["Namespace visitor names per host/service","Single ownership: one team owns one include file","Validate merged config, not just fragments"],"tags":["config","visitor","duplicate","validation","includes"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}