fatedier/frp · error
unmarshal VisitorConfig error: %v
Error message
unmarshal VisitorConfig error: %v
What it means
DecodeVisitorConfigurerJSON fails at decodeJSONWithOptions when the visitor object's fields cannot be unmarshalled into the typed VisitorConfigurer selected by "type". The underlying json error is wrapped as 'unmarshal VisitorConfig error'. The visitor type was recognized, but field names or value types inside the visitor object don't match its struct.
Source
Thrown at pkg/config/v1/decode.go:87
return configurer, nil
}
func DecodeVisitorConfigurerJSON(b []byte, options DecodeOptions) (VisitorConfigurer, error) {
if isJSONNull(b) {
return nil, errors.New("type is required")
}
var env typedEnvelope
if err := jsonx.Unmarshal(b, &env); err != nil {
return nil, err
}
configurer := NewVisitorConfigurerByType(VisitorType(env.Type))
if configurer == nil {
return nil, fmt.Errorf("unknown visitor type: %s", env.Type)
}
if err := decodeJSONWithOptions(b, configurer, options); err != nil {
return nil, fmt.Errorf("unmarshal VisitorConfig error: %v", err)
}
if len(env.Plugin) > 0 && !isJSONNull(env.Plugin) {
plugin, err := DecodeVisitorPluginOptionsJSON(env.Plugin, options)
if err != nil {
return nil, fmt.Errorf("unmarshal visitor plugin error: %v", err)
}
configurer.GetBaseConfig().Plugin = plugin
}
return configurer, nil
}
func DecodeClientPluginOptionsJSON(b []byte, options DecodeOptions) (TypedClientPluginOptions, error) {
if isJSONNull(b) {
return TypedClientPluginOptions{}, nil
}
var env typedEnvelopeView on GitHub (pinned to 6c8a8d0a97)
Solutions
- Read the wrapped error — it identifies the exact field; correct its type (numbers as numbers, strings as strings).
- Align field names with the visitor schema for the declared type (name, secretKey, serverName, bindAddr, bindPort).
- Remove unrecognized fields or relax strict decoding in DecodeOptions.
Example fix
// before
{ "type": "stcp", "bindPort": "6000" }
// after
{ "type": "stcp", "bindPort": 6000 } Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check visitor entries for obviously wrong field types.
func lintVisitorJSON(b []byte) error {
var probe struct {
Type string `json:"type"`
BindPort any `json:"bindPort"`
}
if err := json.Unmarshal(b, &probe); err != nil {
return err
}
if bp, ok := probe.BindPort.(string); ok {
return fmt.Errorf("bindPort must be a number, got string %q", bp)
}
return nil
} Try / catch
if _, err := v1.DecodeClientConfigJSON(b, opts); err != nil {
if strings.Contains(err.Error(), "unmarshal VisitorConfig error") {
// wrapped json error names the field; fix that visitors[] entry
}
return err
} Prevention
- Keep numeric visitor fields numeric in JSON/TOML.
- Use current field names (bindPort, secretKey, serverName).
- Test visitor configs with frpc verify before deploy.
When it happens
Trigger: A visitor entry with wrong-typed fields (e.g. "bindPort": "6000" as a string, or "serverName" given as a number), or unknown fields under strict DecodeOptions. Reached via DecodeClientConfigJSON's visitors loop or a direct DecodeVisitorConfigurerJSON call.
Common situations: Config migration tools emitting strings for numeric fields; stale field names from older frp versions (secretKey casing); strict mode rejecting extra keys.
Related errors
- unmarshal VisitorPluginOptions error: %v
- unmarshal ProxyConfig error: %v
- unknown visitor type: %s
- unmarshal visitor plugin error: %v
- unmarshal ClientPluginOptions error: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/67561d853839dfcb.
Report an issue: GitHub.