fatedier/frp · error · configmgmt.ErrConflict
conflict: %v
Error message
conflict: %v
What it means
Returned by CreateStoreProxy when storeSource.AddProxy reports source.ErrAlreadyExists, meaning a proxy with the same name is already persisted in the store. Wrapped with configmgmt.ErrConflict and surfaced as HTTP 409. Creation is rejected without touching the existing entry.
Source
Thrown at client/config_manager.go:187
}
cfg := storeSource.GetProxy(name)
if cfg == nil {
return nil, fmt.Errorf("%w: proxy %q", configmgmt.ErrNotFound, name)
}
return cfg, nil
}
func (m *serviceConfigManager) CreateStoreProxy(cfg v1.ProxyConfigurer) (v1.ProxyConfigurer, error) {
if err := m.validateStoreProxyConfigurer(cfg); err != nil {
return nil, fmt.Errorf("%w: validation error: %v", configmgmt.ErrInvalidArgument, err)
}
name := cfg.GetBaseConfig().Name
persisted, err := m.withStoreProxyMutationAndReload(name, func(storeSource *source.StoreSource) error {
if err := storeSource.AddProxy(cfg); err != nil {
if errors.Is(err, source.ErrAlreadyExists) {
return fmt.Errorf("%w: %v", configmgmt.ErrConflict, err)
}
return err
}
return nil
})
if err != nil {
return nil, err
}
log.Infof("store: created proxy %q", name)
return persisted, nil
}
func (m *serviceConfigManager) UpdateStoreProxy(name string, cfg v1.ProxyConfigurer) (v1.ProxyConfigurer, error) {
if name == "" {
return nil, fmt.Errorf("%w: proxy name is required", configmgmt.ErrInvalidArgument)
}
if cfg == nil {
return nil, fmt.Errorf("%w: invalid proxy config: type is required", configmgmt.ErrInvalidArgument)View on GitHub (pinned to 6c8a8d0a97)
Solutions
- If you mean to replace it, use PUT /api/store/proxies/{name} (UpdateStoreProxy) instead of POST.
- If the duplicate is accidental, pick a different name or delete the existing one with DELETE /api/store/proxies/{name} first.
- Make provisioning scripts check GET /api/store/proxies/{name} (expect 404) before POSTing, or treat 409 as 'already done'.
Example fix
# before
curl -X POST http://127.0.0.1:7400/api/store/proxies -d '{"type":"tcp","name":"ssh","localPort":22,"remotePort":6022}'
# 409 conflict: proxy ssh already exists
# after
curl -X PUT http://127.0.0.1:7400/api/store/proxies/ssh -d '{"type":"tcp","name":"ssh","localPort":22,"remotePort":6022}' Defensive patterns
Strategy: try-catch
Validate before calling
// Optional pre-check: only POST when GET returns 404.
if _, err := mgr.GetStoreProxy(name); err == nil {
// exists -> update instead
} Try / catch
if _, err := mgr.CreateStoreProxy(cfg); err != nil {
if errors.Is(err, configmgmt.ErrConflict) {
// already provisioned: switch to UpdateStoreProxy or treat as done
}
} Prevention
- Make provisioning idempotent: POST on 404, PUT otherwise
- Treat 409 as success in re-runnable scripts
When it happens
Trigger: POST /api/store/proxies with a name that already exists in the store; re-running an idempotent provisioning script that always POSTs the same proxy; racing two concurrent creates of the same name.
Common situations: Infrastructure tooling (Terraform/Ansible) re-applying the same proxy definition; retry of a request whose response was lost, so the first attempt actually succeeded; migrating a file-defined proxy to the store without deleting the original.
Related errors
- invalid argument: proxy name is required
- not found: proxy %q
- invalid argument: validation error: %v
- invalid argument: invalid proxy config: type is required
- invalid argument: proxy name in URL must match name in body
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/26d0abb29da49772.
Report an issue: GitHub.