{"record":{"id":"fc856b888bbf4c34","repo":"fatedier/frp","slug":"unknown-plugin-type-s","errorCode":null,"errorMessage":"unknown plugin type: %s","messagePattern":"unknown plugin type: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/decode.go","lineNumber":115,"sourceCode":"\treturn configurer, nil\n}\n\nfunc DecodeClientPluginOptionsJSON(b []byte, options DecodeOptions) (TypedClientPluginOptions, error) {\n\tif isJSONNull(b) {\n\t\treturn TypedClientPluginOptions{}, nil\n\t}\n\n\tvar env typedEnvelope\n\tif err := jsonx.Unmarshal(b, &env); err != nil {\n\t\treturn TypedClientPluginOptions{}, err\n\t}\n\tif env.Type == \"\" {\n\t\treturn TypedClientPluginOptions{}, errors.New(\"plugin type is empty\")\n\t}\n\n\tv, ok := clientPluginOptionsTypeMap[env.Type]\n\tif !ok {\n\t\treturn TypedClientPluginOptions{}, fmt.Errorf(\"unknown plugin type: %s\", env.Type)\n\t}\n\toptionsStruct := reflect.New(v).Interface().(ClientPluginOptions)\n\tif err := decodeJSONWithOptions(b, optionsStruct, options); err != nil {\n\t\treturn TypedClientPluginOptions{}, fmt.Errorf(\"unmarshal ClientPluginOptions error: %v\", err)\n\t}\n\treturn TypedClientPluginOptions{\n\t\tType:                env.Type,\n\t\tClientPluginOptions: optionsStruct,\n\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 {","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/decode.go#L97-L133","documentation":"DecodeClientPluginOptionsJSON resolves the plugin options struct from clientPluginOptionsTypeMap keyed by the envelope \"type\". An empty type is rejected earlier ('plugin type is empty'), so this error means a non-empty type string that has no registered options struct — an unknown or unsupported client plugin type. Raised before any plugin field decoding.","triggerScenarios":"Calling DecodeClientPluginOptionsJSON with {\"type\":\"webdav2\",...} where webdav2 is not registered, or a type with whitespace/case drift like \"Static_File\". Any non-empty plugin type absent from the registry for this binary.","commonSituations":"Plugins added in newer frp releases used against an older frp; community forks with extra plugins; typos or casing differences in plugin names in generated configs.","solutions":["Use the exact documented plugin type identifier (snake_case, e.g. static_file, http2https, socks5).","Upgrade (or align) the frp build so the plugin is registered in clientPluginOptionsTypeMap.","Check for stray whitespace or capitalization in the type string."],"exampleFix":"// before\n{\"type\": \"Static_File\", \"localPath\": \"/srv\"}\n\n// after\n{\"type\": \"static_file\", \"localPath\": \"/srv\"}","handlingStrategy":"validation","validationCode":"// Validate plugin type against the registry before decoding.\nfunc validClientPluginName(t string) error {\n\tif strings.TrimSpace(t) == \"\" {\n\t\treturn fmt.Errorf(\"plugin type is empty\")\n\t}\n\tif !isKnownClientPlugin(strings.TrimSpace(t)) {\n\t\treturn fmt.Errorf(\"unknown plugin type %q\", t)\n\t}\n\treturn nil\n}","typeGuard":"func isKnownClientPlugin(t string) bool {\n\tswitch t {\n\tcase \"static_file\", \"http2https\", \"https2http\", \"unix_domain_socket\", \"socks5\", \"proxy_protocol\":\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"if _, err := v1.DecodeClientPluginOptionsJSON(b, opts); err != nil {\n\tif strings.Contains(err.Error(), \"unknown plugin type\") {\n\t\t// correct the type string or upgrade frp to a build that registers it\n\t}\n\treturn err\n}","preventionTips":["Copy plugin names verbatim from the frp docs (snake_case).","Align plugin usage with the deployed frp version.","Trim whitespace in generated type strings."],"tags":["go","frp","config","json","plugin","decode"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}