fatedier/frp · error
decode visitor at index %d: %w
Error message
decode visitor at index %d: %w
What it means
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.
Source
Thrown at pkg/config/v1/decode.go:186
Proxies: make([]TypedProxyConfig, 0, len(raw.Proxies)),
Visitors: make([]TypedVisitorConfig, 0, len(raw.Visitors)),
}
for i, proxyData := range raw.Proxies {
proxyCfg, err := DecodeProxyConfigurerJSON(proxyData, options)
if err != nil {
return ClientConfig{}, fmt.Errorf("decode proxy at index %d: %w", i, err)
}
cfg.Proxies = append(cfg.Proxies, TypedProxyConfig{
Type: proxyCfg.GetBaseConfig().Type,
ProxyConfigurer: proxyCfg,
})
}
for i, visitorData := range raw.Visitors {
visitorCfg, err := DecodeVisitorConfigurerJSON(visitorData, options)
if err != nil {
return ClientConfig{}, fmt.Errorf("decode visitor at index %d: %w", i, err)
}
cfg.Visitors = append(cfg.Visitors, TypedVisitorConfig{
Type: visitorCfg.GetBaseConfig().Type,
VisitorConfigurer: visitorCfg,
})
}
return cfg, nil
}
View on GitHub (pinned to 6c8a8d0a97)
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.
Example fix
# error: decode visitor at index 0: unknown visitor type: tcp # before [[visitors]] type = "tcp" name = "v" # after [[visitors]] type = "stcp" name = "v"
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: validate each visitors[] entry individually.
func lintVisitors(b []byte, opts v1.DecodeOptions) error {
var probe struct {
Visitors []json.RawMessage `json:"visitors"`
}
if err := json.Unmarshal(b, &probe); err != nil {
return err
}
for i, raw := range probe.Visitors {
if _, err := v1.DecodeVisitorConfigurerJSON(raw, opts); err != nil {
return fmt.Errorf("visitors[%d]: %w", i, err)
}
}
return nil
} Try / catch
if _, err := v1.DecodeClientConfigJSON(b, opts); err != nil {
if strings.Contains(err.Error(), "decode visitor at index") {
// index is 0-based into visitors[]; unwrap for the root cause
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Visitor entries copied from proxy examples; stcp/xtcp visitors with field renames after upgrades; one malformed element among many generated visitors.
Related errors
- unknown visitor type: %s
- unmarshal VisitorConfig error: %v
- unmarshal visitor plugin error: %v
- unknown visitor plugin type: %s
- unmarshal VisitorPluginOptions error: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/af7a25d81ed91698.
Report an issue: GitHub.