{"record":{"id":"ba242c210da0e4f4","repo":"fatedier/frp","slug":"decode-proxy-at-index-d-w","errorCode":null,"errorMessage":"decode proxy at index %d: %w","messagePattern":"decode proxy at index (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/decode.go","lineNumber":175,"sourceCode":"\t\tProxies  []jsonx.RawMessage `json:\"proxies,omitempty\"`\n\t\tVisitors []jsonx.RawMessage `json:\"visitors,omitempty\"`\n\t}\n\n\traw := rawClientConfig{}\n\tif err := decodeJSONWithOptions(b, &raw, options); err != nil {\n\t\treturn ClientConfig{}, err\n\t}\n\n\tcfg := ClientConfig{\n\t\tClientCommonConfig: raw.ClientCommonConfig,\n\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","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/decode.go#L157-L193","documentation":"DecodeClientConfigJSON iterates raw.Proxies and decodes each entry with DecodeProxyConfigurerJSON; any per-entry failure is wrapped with the entry's index as 'decode proxy at index %d: %w'. The %w verb preserves the underlying cause chain (unknown type, field mismatch, plugin error), and the index points at which proxies[] element in the client config JSON is broken.","triggerScenarios":"Loading a client config JSON/TOML where the Nth element of proxies[] (0-based) has an unknown type, a wrong-typed field, or a bad plugin block. The wrapped message after the colon is the real cause; the index identifies the array element.","commonSituations":"Large configs where the index is essential to find the bad entry among dozens; programmatic config generation producing one malformed element; partial upgrades where only some entries use newer field names.","solutions":["Parse the index from the message and inspect that element of proxies[] (0-based) in the source config.","Apply the fix indicated by the wrapped cause error (type name, field type, or plugin block) to that element.","Re-run the loader; repeat for any further indexed failures."],"exampleFix":"# error: decode proxy at index 2: unknown proxy type: Tcp\n# before (proxies[2])\n[[proxies]]\ntype = \"Tcp\"\n\n# after\n[[proxies]]\ntype = \"tcp\"","handlingStrategy":"try-catch","validationCode":"// Pre-flight: validate each proxies[] entry individually to report positions, not just the wrapped index error.\nfunc lintProxies(b []byte, opts v1.DecodeOptions) error {\n\tvar probe struct {\n\t\tProxies []json.RawMessage `json:\"proxies\"`\n\t}\n\tif err := json.Unmarshal(b, &probe); err != nil {\n\t\treturn err\n\t}\n\tfor i, raw := range probe.Proxies {\n\t\tif _, err := v1.DecodeProxyConfigurerJSON(raw, opts); err != nil {\n\t\t\treturn fmt.Errorf(\"proxies[%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\tvar msg string\n\tif errors.As(err, &msg); strings.Contains(err.Error(), \"decode proxy at index\") {\n\t\t// extract index from the message; the %w chain holds the root cause for errors.Unwrap\n\t}\n\treturn err\n}","preventionTips":["Decode proxies[] entries one-by-one in tooling so failures carry stable positions.","Use errors.Unwrap/errors.As to reach the root cause under the index wrapper.","Diff generated configs against a known-good schema before rollout."],"tags":["go","frp","config","json","proxy","decode","wrapper"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}