fatedier/frp · error
visitor plugin type is empty
Error message
visitor plugin type is empty
What it means
Thrown by DecodeVisitorPluginOptionsJSON when decoding visitor plugin options JSON that is not null but whose top-level "type" is empty. Visitor plugins (used by stcp/sudp/xtcp visitors for things like TLS termination or secret-key handling) go through the same typedEnvelope mechanism as client plugins, dispatched via visitorPluginOptionsTypeMap; a missing type makes dispatch impossible. This is a strict decoding guard fired before the unknown-visitor-plugin-type check.
Source
Thrown at pkg/config/v1/decode.go:137
return TypedClientPluginOptions{}, fmt.Errorf("unmarshal ClientPluginOptions error: %v", err)
}
return TypedClientPluginOptions{
Type: env.Type,
ClientPluginOptions: optionsStruct,
}, nil
}
func DecodeVisitorPluginOptionsJSON(b []byte, options DecodeOptions) (TypedVisitorPluginOptions, error) {
if isJSONNull(b) {
return TypedVisitorPluginOptions{}, nil
}
var env typedEnvelope
if err := jsonx.Unmarshal(b, &env); err != nil {
return TypedVisitorPluginOptions{}, err
}
if env.Type == "" {
return TypedVisitorPluginOptions{}, errors.New("visitor plugin type is empty")
}
v, ok := visitorPluginOptionsTypeMap[env.Type]
if !ok {
return TypedVisitorPluginOptions{}, fmt.Errorf("unknown visitor plugin type: %s", env.Type)
}
optionsStruct := reflect.New(v).Interface().(VisitorPluginOptions)
if err := decodeJSONWithOptions(b, optionsStruct, options); err != nil {
return TypedVisitorPluginOptions{}, fmt.Errorf("unmarshal VisitorPluginOptions error: %v", err)
}
return TypedVisitorPluginOptions{
Type: env.Type,
VisitorPluginOptions: optionsStruct,
}, nil
}
func DecodeClientConfigJSON(b []byte, options DecodeOptions) (ClientConfig, error) {
type rawClientConfig struct {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Add a valid non-empty "type" to the visitor plugin options JSON if a visitor plugin is intended.
- If no visitor plugin options are needed, remove the options object or set it to JSON null — the decoder returns an empty TypedVisitorPluginOptions without error.
- Verify the exact key spelling "type" (case-sensitive) and that it is at the top level of the options object, not nested.
Example fix
# before [[visitors]] name = "stcpv" type = "stcp" secretKey = "s" bindAddr = "127.0.0.1" bindPort = 9000 [visitors.plugin] # type missing # after [visitors.plugin] type = "https2http" localAddr = ":8443"
Defensive patterns
Strategy: validation
Validate before calling
// before calling DecodeVisitorPluginOptionsJSON
var env struct{ Type string `json:"type"` }
_ = jsonx.Unmarshal(b, &env)
if !isJSONNull(b) && env.Type == "" {
return fmt.Errorf("visitor plugin options must declare a non-empty \"type\" or be null")
} Type guard
func hasVisitorPluginType(b []byte) bool {
if isJSONNull(b) {
return true
}
var env struct{ Type string `json:"type"` }
if err := jsonx.Unmarshal(b, &env); err != nil {
return false
}
return env.Type != ""
} Try / catch
if _, err := v1.DecodeVisitorPluginOptionsJSON(raw, v1.DecodeOptions{}); err != nil {
if strings.Contains(err.Error(), "visitor plugin type is empty") {
// fix the config: add "type" to visitor plugin options or set them to null
}
return err
} Prevention
- Omit the visitor plugin block entirely when no plugin options are needed.
- Never represent 'no options' as an empty JSON object.
- Validate generated configs in CI with frpc verify before deploy.
When it happens
Trigger: Calling DecodeVisitorPluginOptionsJSON(b, options) where the JSON object lacks a "type" key or has type: "" — e.g. a [[visitors]] section in frpc config whose plugin options table was written without a type, or where null was expected but an empty object {} was supplied.
Common situations: Writing an stcp/xtcp visitor entry and omitting or blanking the plugin type, migrating configs from a format that represented 'no plugin' as {} instead of null, or programmatic generation that always emits an options object even when there is no visitor plugin.
Related errors
- plugin type is empty
- unmarshal visitor plugin error: %v
- unknown visitor plugin type: %s
- unmarshal VisitorPluginOptions error: %v
- unmarshal proxy plugin error: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/67a33f2abd4fd801.
Report an issue: GitHub.