{"record":{"id":"2b77f0a3deeb215f","repo":"fatedier/frp","slug":"unknown-visitor-plugin-type-s","errorCode":null,"errorMessage":"unknown visitor plugin type: %s","messagePattern":"unknown visitor plugin type: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/decode.go","lineNumber":142,"sourceCode":"\t}, nil\n}\n\nfunc DecodeVisitorPluginOptionsJSON(b []byte, options DecodeOptions) (TypedVisitorPluginOptions, error) {\n\tif isJSONNull(b) {\n\t\treturn TypedVisitorPluginOptions{}, nil\n\t}\n\n\tvar env typedEnvelope\n\tif err := jsonx.Unmarshal(b, &env); err != nil {\n\t\treturn TypedVisitorPluginOptions{}, err\n\t}\n\tif env.Type == \"\" {\n\t\treturn TypedVisitorPluginOptions{}, errors.New(\"visitor plugin type is empty\")\n\t}\n\n\tv, ok := visitorPluginOptionsTypeMap[env.Type]\n\tif !ok {\n\t\treturn TypedVisitorPluginOptions{}, fmt.Errorf(\"unknown visitor plugin type: %s\", env.Type)\n\t}\n\toptionsStruct := reflect.New(v).Interface().(VisitorPluginOptions)\n\tif err := decodeJSONWithOptions(b, optionsStruct, options); err != nil {\n\t\treturn TypedVisitorPluginOptions{}, fmt.Errorf(\"unmarshal VisitorPluginOptions error: %v\", err)\n\t}\n\treturn TypedVisitorPluginOptions{\n\t\tType:                 env.Type,\n\t\tVisitorPluginOptions: optionsStruct,\n\t}, nil\n}\n\nfunc DecodeClientConfigJSON(b []byte, options DecodeOptions) (ClientConfig, error) {\n\ttype rawClientConfig struct {\n\t\tClientCommonConfig\n\t\tProxies  []jsonx.RawMessage `json:\"proxies,omitempty\"`\n\t\tVisitors []jsonx.RawMessage `json:\"visitors,omitempty\"`\n\t}\n","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/decode.go#L124-L160","documentation":"DecodeVisitorPluginOptionsJSON resolves the visitor plugin options struct from visitorPluginOptionsTypeMap keyed by the envelope \"type\". Empty types are rejected earlier ('visitor plugin type is empty'); this error means a non-empty type with no registered visitor plugin options struct. The visitor plugin registry is much smaller than the client plugin registry, so most proxy plugin names fail here.","triggerScenarios":"Calling DecodeVisitorPluginOptionsJSON with a type such as \"static_file\", \"socks5\", or any other plugin registered only in clientPluginOptionsTypeMap, or a misspelled visitor plugin name.","commonSituations":"Reusing a proxy's plugin block inside a visitor config; assuming plugin parity between proxies and visitors; fork-specific visitor plugins not present in the running build.","solutions":["Use only visitor plugin types registered in this frp build (check visitorPluginOptionsTypeMap for your version).","Remove the plugin block if the visitor doesn't need one.","Move plugin-based tunneling into a proxies[] entry where the full client plugin set is available."],"exampleFix":"// before\n{\"type\": \"static_file\", \"localPath\": \"/srv\"}  // passed to DecodeVisitorPluginOptionsJSON\n\n// after\n// pass it to DecodeClientPluginOptionsJSON from a proxy instead:\n{\"type\": \"tcp\", \"plugin\": {\"type\": \"static_file\", \"localPath\": \"/srv\"}}","handlingStrategy":"validation","validationCode":"// Guard: most plugin types are proxy-only; verify before calling the visitor decoder.\nfunc canDecodeVisitorPlugin(t string) error {\n\tif !isVisitorPluginCandidate(strings.TrimSpace(t)) {\n\t\treturn fmt.Errorf(\"type %q is not a visitor plugin; use DecodeClientPluginOptionsJSON on a proxy instead\", t)\n\t}\n\treturn nil\n}","typeGuard":"func isVisitorPluginCandidate(t string) bool {\n\t// enumerate visitorPluginOptionsTypeMap keys for your frp build here\n\treturn false\n}","tryCatchPattern":"if _, err := v1.DecodeVisitorPluginOptionsJSON(b, opts); err != nil {\n\tif strings.Contains(err.Error(), \"unknown visitor plugin type\") {\n\t\t// move the plugin to a proxy or drop it\n\t}\n\treturn err\n}","preventionTips":["Never assume proxy plugins work as visitor plugins.","Route plugin decoding through the proxy path unless the type is a registered visitor plugin.","Keep a version-pinned allowlist of visitor plugin types."],"tags":["go","frp","config","json","visitor","plugin","decode"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}