fatedier/frp · error

ErrAlreadyExists

ErrAlreadyExists

Error message

already exists

What it means

ErrAlreadyExists is the store source's duplicate sentinel (pkg/config/source/store.go:42). StoreSource create operations reject a proxy or visitor whose name is already present in the persisted store; the config manager re-wraps it as configmgmt.ErrConflict, which the admin API maps to 409.

Source

Thrown at pkg/config/source/store.go:42

	"github.com/fatedier/frp/pkg/util/jsonx"
)

type StoreSourceConfig struct {
	Path string `json:"path"`
}

type storeData struct {
	Proxies  []v1.TypedProxyConfig   `json:"proxies,omitempty"`
	Visitors []v1.TypedVisitorConfig `json:"visitors,omitempty"`
}

type StoreSource struct {
	baseSource
	config StoreSourceConfig
}

var (
	ErrAlreadyExists = errors.New("already exists")
	ErrNotFound      = errors.New("not found")
)

const (
	storeKindProxy   = "proxy"
	storeKindVisitor = "visitor"
)

func NewStoreSource(cfg StoreSourceConfig) (*StoreSource, error) {
	if cfg.Path == "" {
		return nil, fmt.Errorf("path is required")
	}

	s := &StoreSource{
		baseSource: newBaseSource(),
		config:     cfg,
	}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Check existence (Get/Load) before creating, or switch to update semantics
  2. Use a different, unique name
  3. Delete the existing entry first if replacement is intended
  4. In scripts, handle 409/ErrConflict as 'already exists' and continue

Example fix

// before
err := storeSrc.Create(cfg) // already exists

// after
if _, err := storeSrc.Get(kind, name); errors.Is(err, source.ErrNotFound) {
    err = storeSrc.Create(cfg)
} else if err == nil {
    err = storeSrc.Update(name, cfg) // or skip
}
Defensive patterns

Strategy: validation

Validate before calling

if _, err := storeSrc.Get(kind, name); err == nil {
    return fmt.Errorf("%q already exists; use update", name)
} else if !errors.Is(err, source.ErrNotFound) {
    return err
}

Try / catch

err := storeSrc.Create(cfg)
if errors.Is(err, source.ErrAlreadyExists) {
    return storeSrc.Update(name, cfg) // or no-op if idempotent
}

Prevention

When it happens

Trigger: Calling StoreSource/ConfigManager create for a proxy or visitor name that already exists in the store data file; a name that also exists in the file-based config source still counts because the aggregator namespace is name-keyed.

Common situations: Idempotent automation scripts that POST the same proxy on every run; restoring a store backup that already contains the entry; UI double-submit creating the duplicate.

Related errors


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