fatedier/frp · error

unknown visitor config type

Error message

unknown visitor config type

What it means

Error from ValidateVisitorConfigurer: the visitor configurer is not one of *v1.STCPVisitorConfig, *v1.SUDPVisitorConfig, or *v1.XTCPVisitorConfig. Visitors exist only for the secret/p2p proxy types (stcp, sudp, xtcp); any other concrete type — or a nil interface — falls through to this default error after base validation. Usually a programmatic/embedding issue since file configs reject unknown visitor types at decode time.

Source

Thrown at pkg/config/v1/validation/visitor.go:37

	"fmt"
	"slices"

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

func ValidateVisitorConfigurer(c v1.VisitorConfigurer) error {
	base := c.GetBaseConfig()
	if err := validateVisitorBaseConfig(base); err != nil {
		return err
	}

	switch v := c.(type) {
	case *v1.STCPVisitorConfig:
	case *v1.SUDPVisitorConfig:
	case *v1.XTCPVisitorConfig:
		return validateXTCPVisitorConfig(v)
	default:
		return errors.New("unknown visitor config type")
	}
	return nil
}

func validateVisitorBaseConfig(c *v1.VisitorBaseConfig) error {
	if c.Name == "" {
		return errors.New("name is required")
	}

	if c.ServerName == "" {
		return errors.New("server name is required")
	}

	if c.BindPort == 0 {
		return errors.New("bind port is required")
	}
	return nil
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Use one of the three supported visitor configurers: *v1.STCPVisitorConfig, *v1.SUDPVisitorConfig, *v1.XTCPVisitorConfig.
  2. Guard against nil before validating: if c == nil { ... }.
  3. Verify you are pairing the visitor with a matching stcp/sudp/xtcp proxy — tcp/udp/http proxies have no visitors.

Example fix

// before
var vc v1.VisitorConfigurer
err := validation.ValidateVisitorConfigurer(vc)

// after
vc := &v1.STCPVisitorConfig{
    VisitorBaseConfig: &v1.VisitorBaseConfig{
        Name: "stcpv", ServerName: "stcp-srv", BindPort: 9000,
    },
    SecretKey: "secret",
}
err := validation.ValidateVisitorConfigurer(vc)
Defensive patterns

Strategy: type-guard

Validate before calling

func isKnownVisitorType(c v1.VisitorConfigurer) bool {
    switch c.(type) {
    case *v1.STCPVisitorConfig, *v1.SUDPVisitorConfig, *v1.XTCPVisitorConfig:
        return true
    }
    return false
}

Type guard

func isKnownVisitorType(c v1.VisitorConfigurer) bool {
    switch c.(type) {
    case *v1.STCPVisitorConfig, *v1.SUDPVisitorConfig, *v1.XTCPVisitorConfig:
        return true
    default:
        return false
    }
}

Try / catch

if !isKnownVisitorType(cfg) {
    return fmt.Errorf("unsupported visitor configurer %T", cfg)
}
if err := validation.ValidateVisitorConfigurer(cfg); err != nil { return err }

Prevention

When it happens

Trigger: Calling validation.ValidateVisitorConfigurer(c) with a nil visitor configurer, a custom struct implementing VisitorConfigurer, or a value (not pointer) of a visitor config that no longer matches the case types.

Common situations: Embedding frp as a library and constructing visitor configs of the wrong type or leaving them nil; mixed library versions where the visitor type set differs; copying example code that validates a visitor for a proxy type that has no visitor support (e.g. tcp).

Related errors


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