{"record":{"id":"1f8cc87adf59d9c6","repo":"fatedier/frp","slug":"visitor-type-block-q-does-not-match-type-q","errorCode":null,"errorMessage":"visitor type block %q does not match type %q","messagePattern":"visitor type block %q does not match type %q","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"client/http/model/visitor_definition.go","lineNumber":35,"sourceCode":"}\n\nfunc (p *VisitorDefinition) Validate(pathName string, isUpdate bool) error {\n\tif strings.TrimSpace(p.Name) == \"\" {\n\t\treturn fmt.Errorf(\"visitor name is required\")\n\t}\n\tif !IsVisitorType(p.Type) {\n\t\treturn fmt.Errorf(\"invalid visitor type: %s\", p.Type)\n\t}\n\tif isUpdate && pathName != \"\" && pathName != p.Name {\n\t\treturn fmt.Errorf(\"visitor 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 visitor type block is required\")\n\t}\n\tif blockType != p.Type {\n\t\treturn fmt.Errorf(\"visitor type block %q does not match type %q\", blockType, p.Type)\n\t}\n\treturn nil\n}\n\nfunc (p *VisitorDefinition) ToConfigurer() (v1.VisitorConfigurer, error) {\n\tblock, _, _ := p.activeBlock()\n\tif block == nil {\n\t\treturn nil, fmt.Errorf(\"exactly one visitor 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 VisitorDefinitionFromConfigurer(cfg v1.VisitorConfigurer) (VisitorDefinition, error) {\n\tif cfg == nil {","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/client/http/model/visitor_definition.go#L17-L53","documentation":"Returned by VisitorDefinition.Validate when exactly one visitor type block is present but its kind does not match the top-level \"type\" string — e.g. type \"stcp\" while the populated block is \"xtcp\".","triggerScenarios":"{\"name\":\"v\",\"type\":\"xtcp\",\"stcp\":{...}} — the block and type field disagree.","commonSituations":"Editing the \"type\" field without swapping the block; generated configs pulling type and block from different variables; copy-paste between visitor definitions.","solutions":["Make the block key and \"type\" identical (stcp/stcp, sudp/sudp, xtcp/xtcp), lowercase.","Set both from a single source variable in generating code.","Re-check after switching visitor kinds that the old block was removed."],"exampleFix":"// before\n{\"name\": \"v\", \"type\": \"xtcp\", \"stcp\": {\"serverName\": \"svc\"}}\n\n// after\n{\"name\": \"v\", \"type\": \"stcp\", \"stcp\": {\"serverName\": \"svc\"}}","handlingStrategy":"validation","validationCode":"func visitorBlockMatchesType(def map[string]any) bool {\n    for _, k := range []string{\"stcp\",\"sudp\",\"xtcp\"} {\n        if def[k] != nil { return def[\"type\"] == k }\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive type from the populated block key automatically.","Never edit type and block independently.","Lint configs for type/block agreement before deploy."],"tags":["api","validation","visitor","frpc-admin"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}