fatedier/frp · error

ErrConflict

ErrConflict

Error message

conflict

What it means

configmgmt.ErrConflict is the frpc config-manager sentinel for state collisions. CreateProxy/CreateVisitor wrap it (%w) around the underlying store error when a proxy or visitor with the same name already exists — specifically the store source's ErrAlreadyExists bubbling up. The admin HTTP layer maps it to 409 Conflict (client/http/controller.go:64).

Source

Thrown at client/configmgmt/types.go:14

package configmgmt

import (
	"errors"
	"time"

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

var (
	ErrInvalidArgument = errors.New("invalid argument")
	ErrNotFound        = errors.New("not found")
	ErrConflict        = errors.New("conflict")
	ErrStoreDisabled   = errors.New("store disabled")
	ErrApplyConfig     = errors.New("apply config failed")
)

type ConfigManager interface {
	ReloadFromFile(strict bool) error

	ReadConfigFile() (string, error)
	WriteConfigFile(content []byte) error

	GetProxyStatus() []*proxy.WorkingStatus
	IsStoreProxyEnabled(name string) bool
	StoreEnabled() bool

	GetProxyConfig(name string) (v1.ProxyConfigurer, bool)
	GetVisitorConfig(name string) (v1.VisitorConfigurer, bool)

	ListStoreProxies() ([]v1.ProxyConfigurer, error)

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. GET the name first and use PUT (update) instead of POST when it already exists
  2. Pick a unique name — remember file-based and store-based proxies share one namespace per name
  3. In automation, treat errors.Is(err, configmgmt.ErrConflict) as 'already applied' or follow up with an update
  4. Remove the duplicate from the config file before creating it via the API

Example fix

// before
_, err := mgr.CreateProxy(ctx, cfg) // conflict: already exists

// after
if _, err := mgr.GetProxy(ctx, cfg.GetBaseConfig().Name); err != nil {
    _, err = mgr.CreateProxy(ctx, cfg)
} else {
    err = mgr.UpdateProxy(ctx, cfg.GetBaseConfig().Name, cfg)
}
Defensive patterns

Strategy: validation

Validate before calling

if _, err := mgr.GetProxy(ctx, cfg.GetBaseConfig().Name); err == nil {
    // exists -> update path, avoid Create conflict
} else if !errors.Is(err, configmgmt.ErrNotFound) {
    return err
}

Try / catch

if _, err := mgr.CreateProxy(ctx, cfg); err != nil {
    if errors.Is(err, configmgmt.ErrConflict) {
        return mgr.UpdateProxy(ctx, cfg.GetBaseConfig().Name, cfg)
    }
    return err
}

Prevention

When it happens

Trigger: POST /api/proxy or /api/visitor with a name that already exists in the store source or collides with a same-named entry merged from the config file (the aggregator dedupes by name, so creation of a duplicate is rejected).

Common situations: Re-running an automation script that creates proxies without checking existence first; a proxy defined in frpc.toml sharing a name with one being added via the API; retrying a create after a timeout when the first attempt actually succeeded.

Related errors


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