fatedier/frp · error

ErrHealthCheckType

ErrHealthCheckType

Error message

error health check type

What it means

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.

Source

Thrown at client/health/health.go:31

// limitations under the License.

package health

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"strings"
	"time"

	v1 "github.com/fatedier/frp/pkg/config/v1"
	"github.com/fatedier/frp/pkg/util/xlog"
)

var ErrHealthCheckType = errors.New("error health check type")

type Monitor struct {
	checkType      string
	interval       time.Duration
	timeout        time.Duration
	maxFailedTimes int

	// For tcp
	addr string

	// For http
	url            string
	header         http.Header
	failedTimes    uint64
	statusOK       bool
	statusNormalFn func()
	statusFailedFn func()

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Set healthCheck.type to exactly 'tcp' or 'http' in the proxy config
  2. For HTTPS targets use type 'http' — the checker does a plain HTTP request to the given path/port
  3. Remove the healthCheck block entirely if no probe is wanted
  4. After fixing, reload frpc so a new Monitor is built from the corrected config

Example fix

# before
[[proxies]]
name = "web"
healthCheck.type = "https"  # -> ErrHealthCheckType every cycle

# after
[[proxies]]
name = "web"
healthCheck.type = "http"
healthCheck.path = "/healthz"
Defensive patterns

Strategy: validation

Validate before calling

func validHealthCheckType(t string) bool { return t == "" || t == "tcp" || t == "http" }

if !validHealthCheckType(cfg.HealthCheck.Type) {
    return fmt.Errorf("healthCheck.type must be tcp or http, got %q", cfg.HealthCheck.Type)
}

Try / catch

err := monitor.DoCheck(ctx)
if errors.Is(err, health.ErrHealthCheckType) {
    // permanent misconfiguration — disable the monitor rather than counting failures
    monitor.Stop()
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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