{"record":{"id":"af7a25d81ed91698","repo":"fatedier/frp","slug":"decode-visitor-at-index-d-w","errorCode":null,"errorMessage":"decode visitor at index %d: %w","messagePattern":"decode visitor at index (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/decode.go","lineNumber":186,"sourceCode":"\t\tProxies:            make([]TypedProxyConfig, 0, len(raw.Proxies)),\n\t\tVisitors:           make([]TypedVisitorConfig, 0, len(raw.Visitors)),\n\t}\n\n\tfor i, proxyData := range raw.Proxies {\n\t\tproxyCfg, err := DecodeProxyConfigurerJSON(proxyData, options)\n\t\tif err != nil {\n\t\t\treturn ClientConfig{}, fmt.Errorf(\"decode proxy at index %d: %w\", i, err)\n\t\t}\n\t\tcfg.Proxies = append(cfg.Proxies, TypedProxyConfig{\n\t\t\tType:            proxyCfg.GetBaseConfig().Type,\n\t\t\tProxyConfigurer: proxyCfg,\n\t\t})\n\t}\n\n\tfor i, visitorData := range raw.Visitors {\n\t\tvisitorCfg, err := DecodeVisitorConfigurerJSON(visitorData, options)\n\t\tif err != nil {\n\t\t\treturn ClientConfig{}, fmt.Errorf(\"decode visitor at index %d: %w\", i, err)\n\t\t}\n\t\tcfg.Visitors = append(cfg.Visitors, TypedVisitorConfig{\n\t\t\tType:              visitorCfg.GetBaseConfig().Type,\n\t\t\tVisitorConfigurer: visitorCfg,\n\t\t})\n\t}\n\n\treturn cfg, nil\n}\n","sourceCodeStart":168,"sourceCodeEnd":196,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/decode.go#L168-L196","documentation":"DecodeClientConfigJSON iterates raw.Visitors and decodes each entry with DecodeVisitorConfigurerJSON; any per-entry failure is wrapped with the entry's index as 'decode visitor at index %d: %w'. The %w chain preserves the root cause (unknown visitor type, field mismatch, plugin error) while the index identifies which visitors[] element failed.","triggerScenarios":"Loading a client config where the Nth element of visitors[] (0-based) has an unsupported visitor type, a wrong-typed field, or an invalid plugin block. Reached whenever DecodeClientConfigJSON processes the visitors array.","commonSituations":"Visitor entries copied from proxy examples; stcp/xtcp visitors with field renames after upgrades; one malformed element among many generated visitors.","solutions":["Use the reported index to locate the exact visitors[] element (0-based) in the config.","Fix that element per the wrapped cause error (type value, field type/name, or plugin block).","Reload and address any subsequent indexed errors."],"exampleFix":"# error: decode visitor at index 0: unknown visitor type: tcp\n# before\n[[visitors]]\ntype = \"tcp\"\nname = \"v\"\n\n# after\n[[visitors]]\ntype = \"stcp\"\nname = \"v\"","handlingStrategy":"try-catch","validationCode":"// Pre-flight: validate each visitors[] entry individually.\nfunc lintVisitors(b []byte, opts v1.DecodeOptions) error {\n\tvar probe struct {\n\t\tVisitors []json.RawMessage `json:\"visitors\"`\n\t}\n\tif err := json.Unmarshal(b, &probe); err != nil {\n\t\treturn err\n\t}\n\tfor i, raw := range probe.Visitors {\n\t\tif _, err := v1.DecodeVisitorConfigurerJSON(raw, opts); err != nil {\n\t\t\treturn fmt.Errorf(\"visitors[%d]: %w\", i, err)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := v1.DecodeClientConfigJSON(b, opts); err != nil {\n\tif strings.Contains(err.Error(), \"decode visitor at index\") {\n\t\t// index is 0-based into visitors[]; unwrap for the root cause\n\t}\n\treturn err\n}","preventionTips":["Treat the index as 0-based and inspect that exact visitors[] element.","Unwrap the error chain to find the underlying type/field/plugin cause.","Validate visitor configs in CI with the production frp version."],"tags":["go","frp","config","json","visitor","decode","wrapper"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}