fatedier/frp · error

type is required

Error message

type is required

What it means

DecodeProxyConfigurerJSON (pkg/config/v1/decode.go:46) rejects a JSON null payload for a proxy configurer with 'type is required'. The decoder needs a typed envelope {"type": "tcp"|..., ...} to select the concrete config struct; null carries no type, so there is nothing to instantiate. (A present-but-unknown type instead yields 'unknown proxy type: ...'.)

Source

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

func decodeJSONWithOptions(b []byte, out any, options DecodeOptions) error {
	return jsonx.UnmarshalWithOptions(b, out, jsonx.DecodeOptions{
		RejectUnknownMembers: options.DisallowUnknownFields,
	})
}

func isJSONNull(b []byte) bool {
	return len(b) == 0 || string(b) == "null"
}

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 {

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Send an object with a valid type field: {"type":"tcp","name":"ssh",...}
  2. Omit the entry entirely instead of serializing null
  3. Filter null entries out of arrays before decoding
  4. If a typed nil can reach the encoder, skip marshaling it (omitempty / nil check)

Example fix

// before
raw := []byte("null")
cfg, err := v1.DecodeProxyConfigurerJSON(raw, opts) // type is required

// after
raw := []byte(`{"type":"tcp","name":"ssh","remotePort":22}`)
cfg, err := v1.DecodeProxyConfigurerJSON(raw, opts)
Defensive patterns

Strategy: type-guard

Validate before calling

if len(b) == 0 || string(b) == "null" {
    return fmt.Errorf("proxy entry is null; an object with \"type\" is required")
}

Type guard

func isDecodableProxyJSON(b []byte) bool {
    s := strings.TrimSpace(string(b))
    return s != "" && s != "null"
}

Try / catch

cfg, err := v1.DecodeProxyConfigurerJSON(b, opts)
if err != nil {
    if strings.Contains(err.Error(), "type is required") {
        return nil, fmt.Errorf("null proxy config in payload at %s", path)
    }
    return nil, err
}

Prevention

When it happens

Trigger: Decoding a proxy entry that is literal null — e.g. an API/store payload where a proxy slot was set to null, or json.Marshal of a nil pointer written into the store file then read back and decoded.

Common situations: Automation writing JSON null placeholders into proxy arrays; partial updates serialized from maps with nil values; hand-edited store/config JSON containing null entries.

Related errors


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