{"record":{"id":"05d118d3ec1f0423","repo":"fatedier/frp","slug":"unmarshal-proxy-plugin-error-v","errorCode":null,"errorMessage":"unmarshal proxy plugin error: %v","messagePattern":"unmarshal proxy plugin error: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/decode.go","lineNumber":65,"sourceCode":"\t}\n\n\tvar env typedEnvelope\n\tif err := jsonx.Unmarshal(b, &env); err != nil {\n\t\treturn nil, err\n\t}\n\n\tconfigurer := NewProxyConfigurerByType(ProxyType(env.Type))\n\tif configurer == nil {\n\t\treturn nil, fmt.Errorf(\"unknown proxy type: %s\", env.Type)\n\t}\n\tif err := decodeJSONWithOptions(b, configurer, options); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal ProxyConfig error: %v\", err)\n\t}\n\n\tif len(env.Plugin) > 0 && !isJSONNull(env.Plugin) {\n\t\tplugin, err := DecodeClientPluginOptionsJSON(env.Plugin, options)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unmarshal proxy plugin error: %v\", err)\n\t\t}\n\t\tconfigurer.GetBaseConfig().Plugin = plugin\n\t}\n\treturn configurer, nil\n}\n\nfunc DecodeVisitorConfigurerJSON(b []byte, options DecodeOptions) (VisitorConfigurer, error) {\n\tif isJSONNull(b) {\n\t\treturn nil, errors.New(\"type is required\")\n\t}\n\n\tvar env typedEnvelope\n\tif err := jsonx.Unmarshal(b, &env); err != nil {\n\t\treturn nil, err\n\t}\n\n\tconfigurer := NewVisitorConfigurerByType(VisitorType(env.Type))\n\tif configurer == nil {","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/decode.go#L47-L83","documentation":"After a proxy's own fields decode successfully, DecodeProxyConfigurerJSON decodes the embedded \"plugin\" object with DecodeClientPluginOptionsJSON. If that plugin object has a \"type\" that is not in clientPluginOptionsTypeMap (unknown plugin), or its fields fail decoding, the error is wrapped as 'unmarshal proxy plugin error'. The proxy is otherwise valid; only its plugin stanza is broken.","triggerScenarios":"A proxy with a plugin block whose type is misspelled or unsupported in this build (e.g. plugin.type=\"staticfile\" instead of \"static_file\", or a plugin not compiled in), or plugin option fields of the wrong type. Triggered via DecodeClientConfigJSON or direct DecodeProxyConfigurerJSON on such a proxy.","commonSituations":"Plugin name typos (static_file vs staticfile); using a plugin introduced in a newer frp version against an older binary; plugin options renamed between versions (e.g. plugin-local-path vs localPath after the TOML migration).","solutions":["Set plugin.type to a supported plugin name exactly as documented (e.g. static_file, http2https, unix_domain_socket, socks5, https2http...).","Fix the wrapped error's named plugin field (wrong type or renamed option).","Upgrade frp if the plugin exists only in a newer release, or drop the plugin block."],"exampleFix":"// before\n{ \"type\": \"tcp\", \"plugin\": { \"type\": \"staticfile\", \"localPath\": \"/srv\" } }\n\n// after\n{ \"type\": \"tcp\", \"plugin\": { \"type\": \"static_file\", \"localPath\": \"/srv\" } }","handlingStrategy":"validation","validationCode":"// Verify the proxy's plugin block type is registered before decoding the client config.\nfunc validClientPluginType(pluginJSON []byte) error {\n\tvar env struct{ Type string `json:\"type\"` }\n\tif err := json.Unmarshal(pluginJSON, &env); err != nil {\n\t\treturn err\n\t}\n\tswitch env.Type {\n\tcase \"static_file\", \"http2https\", \"https2http\", \"unix_domain_socket\", \"socks5\", \"proxy_protocol\":\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"plugin type %q not supported by this frp build\", env.Type)\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.DecodeClientConfigJSON(b, opts); err != nil {\n\tif strings.Contains(err.Error(), \"unmarshal proxy plugin error\") {\n\t\t// inspect plugin.type and plugin fields of the indexed proxy\n\t}\n\treturn err\n}","preventionTips":["Use exact snake_case plugin names from the docs.","Match plugin names to your frp version; upgrade both frpc and frps together.","Keep plugin options under plugin.* keys with current field names."],"tags":["go","frp","config","json","plugin","decode"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}