fatedier/frp · error

unknown proxy type: %s

Error message

unknown proxy type: %s

What it means

DecodeProxyConfigurerJSON in pkg/config/v1/decode.go dispatches on the JSON envelope's "type" field to construct the right ProxyConfigurer via NewProxyConfigurerByType. If the type string does not match any registered proxy type (tcp, udp, xtcp, stcp, sudp, tcpmux, http, https, plugin variants), the factory returns nil and this error names the offending type. It fires before any field-level decoding of the proxy object.

Source

Thrown at pkg/config/v1/decode.go:56

type typedEnvelope struct {
	Type   string           `json:"type"`
	Plugin jsonx.RawMessage `json:"plugin,omitempty"`
}

func DecodeProxyConfigurerJSON(b []byte, options DecodeOptions) (ProxyConfigurer, 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 := NewProxyConfigurerByType(ProxyType(env.Type))
	if configurer == nil {
		return nil, fmt.Errorf("unknown proxy type: %s", env.Type)
	}
	if err := decodeJSONWithOptions(b, configurer, options); err != nil {
		return nil, fmt.Errorf("unmarshal ProxyConfig error: %v", err)
	}

	if len(env.Plugin) > 0 && !isJSONNull(env.Plugin) {
		plugin, err := DecodeClientPluginOptionsJSON(env.Plugin, options)
		if err != nil {
			return nil, fmt.Errorf("unmarshal proxy plugin error: %v", err)
		}
		configurer.GetBaseConfig().Plugin = plugin
	}
	return configurer, nil
}

func DecodeVisitorConfigurerJSON(b []byte, options DecodeOptions) (VisitorConfigurer, error) {
	if isJSONNull(b) {
		return nil, errors.New("type is required")

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Set "type" to a supported built-in value: tcp, udp, xtcp, stcp, sudp, tcpmux, http, or https.
  2. Check case and whitespace — the match is exact and lowercase.
  3. For plugin-based proxies, keep type as the transport (e.g. "tcp") and put the plugin name under plugin.type, not in the proxy type field.

Example fix

// before
{ "type": "Tcp", "localPort": 80 }

// after
{ "type": "tcp", "localPort": 80 }
Defensive patterns

Strategy: validation

Validate before calling

// Check the proxy type against known types before decoding.
var knownProxyTypes = map[string]bool{
	"tcp": true, "udp": true, "xtcp": true, "stcp": true,
	"sudp": true, "tcpmux": true, "http": true, "https": true,
}
func validProxyType(t string) error {
	if !knownProxyTypes[t] {
		return fmt.Errorf("unknown proxy type %q; want one of tcp,udp,xtcp,stcp,sudp,tcpmux,http,https", t)
	}
	return nil
}

Type guard

func isKnownProxyType(t string) bool { return knownProxyTypes[t] }

Try / catch

if cfg, err := v1.DecodeProxyConfigurerJSON(b, opts); err != nil {
	if strings.Contains(err.Error(), "unknown proxy type") {
		// surface the type string and list supported types to the operator
	}
	return err
}

Prevention

When it happens

Trigger: Calling DecodeProxyConfigurerJSON (directly, or via DecodeClientConfigJSON over a proxies[] entry) with a proxy whose "type" is misspelled or unregistered, e.g. "Tcp" (case-sensitive), "web", "grpc", or a plugin name placed in "type" instead of "plugin.type". Also when the JSON lacks quotes/has a broken type field value that still unmarshals.

Common situations: Upgrading from frp versions whose accepted type sets differ; using a third-party proxy type from a plugin ecosystem not compiled into this build; case mismatches ('TCP' vs 'tcp'); putting the plugin name into proxy "type" (e.g. type="static_file" instead of type="tcp" + plugin settings).

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/1d75deb09a9db364. Report an issue: GitHub.