fatedier/frp · error

proxy type block %q does not match type %q

Error message

proxy type block %q does not match type %q

What it means

Returned by ProxyDefinition.Validate when the definition contains exactly one type block, but the block's kind disagrees with the top-level "type" string — e.g. type "tcp" with an "http" block. The block and the type field must denote the same proxy kind.

Source

Thrown at client/http/model/proxy_definition.go:40

}

func (p *ProxyDefinition) Validate(pathName string, isUpdate bool) error {
	if strings.TrimSpace(p.Name) == "" {
		return fmt.Errorf("proxy name is required")
	}
	if !IsProxyType(p.Type) {
		return fmt.Errorf("invalid proxy type: %s", p.Type)
	}
	if isUpdate && pathName != "" && pathName != p.Name {
		return fmt.Errorf("proxy name in URL must match name in body")
	}

	_, blockType, blockCount := p.activeBlock()
	if blockCount != 1 {
		return fmt.Errorf("exactly one proxy type block is required")
	}
	if blockType != p.Type {
		return fmt.Errorf("proxy type block %q does not match type %q", blockType, p.Type)
	}
	return nil
}

func (p *ProxyDefinition) ToConfigurer() (v1.ProxyConfigurer, error) {
	block, _, _ := p.activeBlock()
	if block == nil {
		return nil, fmt.Errorf("exactly one proxy type block is required")
	}

	cfg := block
	cfg.GetBaseConfig().Name = p.Name
	cfg.GetBaseConfig().Type = p.Type
	return cfg, nil
}

func ProxyDefinitionFromConfigurer(cfg v1.ProxyConfigurer) (ProxyDefinition, error) {
	if cfg == nil {

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Make "type" match the block key: type tcp goes with the "tcp" block, type http with the "http" block, etc.
  2. Keep type lowercase when correcting it.
  3. Derive "type" from the block you set, ideally in one place in the generating code.

Example fix

// before
{"name": "web", "type": "tcp", "http": {"localPort": 80}}

// after
{"name": "web", "type": "http", "http": {"localPort": 80, "customDomains": ["example.com"]}}
Defensive patterns

Strategy: validation

Validate before calling

func blockMatchesType(def map[string]any) bool {
    for _, k := range []string{"tcp","udp","http","https","tcpmux","stcp","sudp","xtcp"} {
        if def[k] != nil { return def["type"] == k }
    }
    return false
}

Prevention

When it happens

Trigger: {"name":"x","type":"tcp","http":{"localPort":80}} — the http block is set but type says tcp. Also from case errors in "type" (e.g. "Http") that survive IsProxyType in some forks, or hand-edited JSON where one field was updated and the other was not.

Common situations: Changing the "type" field without swapping the block (or vice versa); generated configs where type and block come from different sources; copy-paste between definitions.

Related errors


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