fatedier/frp · error · configmgmt.ErrConflict
conflict
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.
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: invalid proxy config: type is required
- invalid argument: proxy name in URL must match name in body
- invalid argument: proxy name is required
- invalid argument: validation error
- not found: proxy
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/26d0abb29da49772.
Report an issue: GitHub.
Appendix: 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)