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
- Check existence (Get/Load) before creating, or switch to update semantics
- Use a different, unique name
- Delete the existing entry first if replacement is intended
- 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
- Implement create-or-update (upsert) in store automation
- Check the store file contents when duplicates surprise you
- Beware name collisions between file-source and store-source entries
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
- ErrConflict
- invalid argument: frpc has no config file path
- invalid argument: %v
- apply config failed: %v
- conflict: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/af6c8afc01246c9e.
Report an issue: GitHub.