{"record":{"id":"a2d67233a206ecfd","repo":"fatedier/frp","slug":"errhealthchecktype","errorCode":"ErrHealthCheckType","errorMessage":"error health check type","messagePattern":"error health check type","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/health/health.go","lineNumber":31,"sourceCode":"// limitations under the License.\n\npackage health\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\tv1 \"github.com/fatedier/frp/pkg/config/v1\"\n\t\"github.com/fatedier/frp/pkg/util/xlog\"\n)\n\nvar ErrHealthCheckType = errors.New(\"error health check type\")\n\ntype Monitor struct {\n\tcheckType      string\n\tinterval       time.Duration\n\ttimeout        time.Duration\n\tmaxFailedTimes int\n\n\t// For tcp\n\taddr string\n\n\t// For http\n\turl            string\n\theader         http.Header\n\tfailedTimes    uint64\n\tstatusOK       bool\n\tstatusNormalFn func()\n\tstatusFailedFn func()\n","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/client/health/health.go#L13-L49","documentation":"client/health's Monitor.doCheck returns ErrHealthCheckType when cfg.Type is neither 'tcp' nor 'http' (health.go:148). The health checker only implements TCP dial and HTTP GET probes; any other type string makes every check cycle fail, which drives statusFailedFn and marks the proxy unhealthy.","triggerScenarios":"A proxy config with healthCheck.type set to something other than tcp/http (e.g. 'https', 'grpc', 'udp', or a typo like 'TCP' with different casing depending on validation) — each checkWorker tick calls doCheck and gets this error, so the proxy is reported failed after maxFailed times.","commonSituations":"Copying a healthCheck block from another tool that supports more probe types; assuming 'https' works because the URL scheme exists; case or whitespace differences in the type string.","solutions":["Set healthCheck.type to exactly 'tcp' or 'http' in the proxy config","For HTTPS targets use type 'http' — the checker does a plain HTTP request to the given path/port","Remove the healthCheck block entirely if no probe is wanted","After fixing, reload frpc so a new Monitor is built from the corrected config"],"exampleFix":"# before\n[[proxies]]\nname = \"web\"\nhealthCheck.type = \"https\"  # -> ErrHealthCheckType every cycle\n\n# after\n[[proxies]]\nname = \"web\"\nhealthCheck.type = \"http\"\nhealthCheck.path = \"/healthz\"","handlingStrategy":"validation","validationCode":"func validHealthCheckType(t string) bool { return t == \"\" || t == \"tcp\" || t == \"http\" }\n\nif !validHealthCheckType(cfg.HealthCheck.Type) {\n    return fmt.Errorf(\"healthCheck.type must be tcp or http, got %q\", cfg.HealthCheck.Type)\n}","typeGuard":null,"tryCatchPattern":"err := monitor.DoCheck(ctx)\nif errors.Is(err, health.ErrHealthCheckType) {\n    // permanent misconfiguration — disable the monitor rather than counting failures\n    monitor.Stop()\n}","preventionTips":["Whitelist type to tcp/http in config linting before deploy","Remember 'https' targets use type 'http'","Health-check config errors are permanent: fail validation at load time, not per-tick"],"tags":["go","frpc","health-check","configuration"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}