{"record":{"id":"e8fd88a37752face","repo":"fatedier/frp","slug":"proxy-type-block-q-does-not-match-type-q","errorCode":null,"errorMessage":"proxy type block %q does not match type %q","messagePattern":"proxy type block %q does not match type %q","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"client/http/model/proxy_definition.go","lineNumber":40,"sourceCode":"}\n\nfunc (p *ProxyDefinition) Validate(pathName string, isUpdate bool) error {\n\tif strings.TrimSpace(p.Name) == \"\" {\n\t\treturn fmt.Errorf(\"proxy name is required\")\n\t}\n\tif !IsProxyType(p.Type) {\n\t\treturn fmt.Errorf(\"invalid proxy type: %s\", p.Type)\n\t}\n\tif isUpdate && pathName != \"\" && pathName != p.Name {\n\t\treturn fmt.Errorf(\"proxy name in URL must match name in body\")\n\t}\n\n\t_, blockType, blockCount := p.activeBlock()\n\tif blockCount != 1 {\n\t\treturn fmt.Errorf(\"exactly one proxy type block is required\")\n\t}\n\tif blockType != p.Type {\n\t\treturn fmt.Errorf(\"proxy type block %q does not match type %q\", blockType, p.Type)\n\t}\n\treturn nil\n}\n\nfunc (p *ProxyDefinition) ToConfigurer() (v1.ProxyConfigurer, error) {\n\tblock, _, _ := p.activeBlock()\n\tif block == nil {\n\t\treturn nil, fmt.Errorf(\"exactly one proxy type block is required\")\n\t}\n\n\tcfg := block\n\tcfg.GetBaseConfig().Name = p.Name\n\tcfg.GetBaseConfig().Type = p.Type\n\treturn cfg, nil\n}\n\nfunc ProxyDefinitionFromConfigurer(cfg v1.ProxyConfigurer) (ProxyDefinition, error) {\n\tif cfg == nil {","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/client/http/model/proxy_definition.go#L22-L58","documentation":"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.","triggerScenarios":"{\"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.","commonSituations":"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.","solutions":["Make \"type\" match the block key: type tcp goes with the \"tcp\" block, type http with the \"http\" block, etc.","Keep type lowercase when correcting it.","Derive \"type\" from the block you set, ideally in one place in the generating code."],"exampleFix":"// before\n{\"name\": \"web\", \"type\": \"tcp\", \"http\": {\"localPort\": 80}}\n\n// after\n{\"name\": \"web\", \"type\": \"http\", \"http\": {\"localPort\": 80, \"customDomains\": [\"example.com\"]}}","handlingStrategy":"validation","validationCode":"func blockMatchesType(def map[string]any) bool {\n    for _, k := range []string{\"tcp\",\"udp\",\"http\",\"https\",\"tcpmux\",\"stcp\",\"sudp\",\"xtcp\"} {\n        if def[k] != nil { return def[\"type\"] == k }\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set type and block together: type := blockKey at definition-build time.","Never hand-edit one of the two fields independently.","Lint generated configs for type/block agreement."],"tags":["api","validation","proxy-type","frpc-admin"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}