fatedier/frp · error · ErrRouterConfigConflict

router config conflict

Error message

router config conflict

What it means

ErrRouterConfigConflict is returned by Routers.Add (pkg/util/vhost/router.go:43) when a route with the same (domain, location, httpUser) triple is already registered. frps calls this when an HTTP/HTTPS/TCPMux proxy tries to bind a vhost route; duplicate registration is rejected to keep routing unambiguous.

Source

Thrown at pkg/util/vhost/router.go:11

package vhost

import (
	"cmp"
	"errors"
	"slices"
	"strings"
	"sync"
)

var ErrRouterConfigConflict = errors.New("router config conflict")

type routerByHTTPUser map[string][]*Router

type Routers struct {
	indexByDomain map[string]routerByHTTPUser

	mutex sync.RWMutex
}

type Router struct {
	domain   string
	location string
	httpUser string

	// store any object here
	payload any
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Give each proxy a unique domain/subdomain (or distinct locations) — only one proxy may own a given host+location+user triple
  2. If you cloned a client, stop the duplicate frpc instance or edit its config to use different domains
  3. If a stale route is suspected, wait for frps to reap the dead connection's proxies or restart frps
  4. For load balancing across clients, use the group feature with the same group name instead of duplicate routes

Example fix

# before — two clients both claim web.example.com
[[proxies]]
name = "web"
type = "http"
customDomains = ["web.example.com"]

# after — load-balance via group instead of duplicates
[[proxies]]
name = "web-1"
type = "http"
group = "web"
groupKey = "shared"
customDomains = ["web.example.com"]
Defensive patterns

Strategy: validation

Validate before calling

// frps side, before adding a vhost route
if err := vhostRouter.Add(domain, location, httpUser, payload); err != nil {
    if errors.Is(err, vhost.ErrRouterConfigConflict) {
        return fmt.Errorf("route %s%s (user %q) already registered by another proxy", domain, location, httpUser)
    }
}

Type guard

func isRouterConflict(err error) bool {
    return errors.Is(err, vhost.ErrRouterConfigConflict)
}

Try / catch

if err := router.Add(dom, loc, user, payload); errors.Is(err, vhost.ErrRouterConfigConflict) {
    // reject the duplicate proxy registration with a clear message to the client
}

Prevention

When it happens

Trigger: Two frpc clients (or two proxies in one client) both claiming the same customDomain/subdomain with the same location and httpUser; a stale proxy not yet cleaned up when a new login re-registers the same route; tcpmux HTTPConnect with duplicate passthrough mapping.

Common situations: Running the same frpc config on two machines pointed at one frps (e.g. after cloning a VM); re-login after connection flapping where the old route has not been released; one proxy uses customDomains overlapping another's wildcard route with identical location.

Related errors


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