fatedier/frp · error
exactly one proxy type block is required
Error message
exactly one proxy type block is required
What it means
Returned by ProxyDefinition.Validate when the number of populated type blocks (tcp/udp/http/https/tcpmux/stcp/sudp/xtcp) in a proxy definition is not exactly one. Both zero blocks (only top-level name/type given) and two or more blocks (e.g. both "tcp" and "http" set) produce this error.
Source
Thrown at client/http/model/proxy_definition.go:37
STCP *v1.STCPProxyConfig `json:"stcp,omitempty"`
SUDP *v1.SUDPProxyConfig `json:"sudp,omitempty"`
XTCP *v1.XTCPProxyConfig `json:"xtcp,omitempty"`
}
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
}View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Include exactly one type block matching the "type" field, e.g. "tcp": {"localPort": 22, "remotePort": 6000} for type tcp.
- When changing proxy type, replace (not merge) the definition so the old block is absent/omitted (use omitempty semantics: null or drop the key).
- Validate the request body shape before sending: exactly one non-null block among the eight keys.
Example fix
// before
{"name": "ssh", "type": "tcp"}
// after
{"name": "ssh", "type": "tcp", "tcp": {"localPort": 22, "remotePort": 6000}} Defensive patterns
Strategy: validation
Validate before calling
func countBlocks(def map[string]any) int {
n := 0
for _, k := range []string{"tcp","udp","http","https","tcpmux","stcp","sudp","xtcp"} {
if def[k] != nil { n++ }
}
return n
}
// require countBlocks(def) == 1 before POST Prevention
- Build definitions from a struct with omitempty so absent blocks serialize as omitted.
- When switching proxy type, construct a fresh definition instead of mutating.
- Add a unit test asserting exactly one block per generated definition.
When it happens
Trigger: POST/PUT /api/proxies with {"name":"x","type":"tcp"} but no "tcp" block; or {"name":"x","type":"tcp","tcp":{...},"http":{...}} where two blocks are non-null.
Common situations: Forgetting the nested config block entirely; switching a proxy from http to tcp by changing "type" and adding the new block while forgetting to null out the old one; JSON merge of definitions accumulating blocks.
Related errors
- invalid proxy type: %s
- proxy type block %q does not match type %q
- proxy name is required
- proxy name in URL must match name in body
- visitor name is required
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/c1433e4d6b0ea10c.
Report an issue: GitHub.