{"record":{"id":"073f7df2f54fea63","repo":"fatedier/frp","slug":"errconflict","errorCode":"ErrConflict","errorMessage":"conflict","messagePattern":"conflict","errorType":"error_code","errorClass":null,"httpStatus":409,"severity":"error","filePath":"client/configmgmt/types.go","lineNumber":14,"sourceCode":"package configmgmt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/fatedier/frp/client/proxy\"\n\tv1 \"github.com/fatedier/frp/pkg/config/v1\"\n)\n\nvar (\n\tErrInvalidArgument = errors.New(\"invalid argument\")\n\tErrNotFound        = errors.New(\"not found\")\n\tErrConflict        = errors.New(\"conflict\")\n\tErrStoreDisabled   = errors.New(\"store disabled\")\n\tErrApplyConfig     = errors.New(\"apply config failed\")\n)\n\ntype ConfigManager interface {\n\tReloadFromFile(strict bool) error\n\n\tReadConfigFile() (string, error)\n\tWriteConfigFile(content []byte) error\n\n\tGetProxyStatus() []*proxy.WorkingStatus\n\tIsStoreProxyEnabled(name string) bool\n\tStoreEnabled() bool\n\n\tGetProxyConfig(name string) (v1.ProxyConfigurer, bool)\n\tGetVisitorConfig(name string) (v1.VisitorConfigurer, bool)\n\n\tListStoreProxies() ([]v1.ProxyConfigurer, error)","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/client/configmgmt/types.go#L1-L32","documentation":"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).","triggerScenarios":"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).","commonSituations":"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.","solutions":["GET the name first and use PUT (update) instead of POST when it already exists","Pick a unique name — remember file-based and store-based proxies share one namespace per name","In automation, treat errors.Is(err, configmgmt.ErrConflict) as 'already applied' or follow up with an update","Remove the duplicate from the config file before creating it via the API"],"exampleFix":"// before\n_, err := mgr.CreateProxy(ctx, cfg) // conflict: already exists\n\n// after\nif _, err := mgr.GetProxy(ctx, cfg.GetBaseConfig().Name); err != nil {\n    _, err = mgr.CreateProxy(ctx, cfg)\n} else {\n    err = mgr.UpdateProxy(ctx, cfg.GetBaseConfig().Name, cfg)\n}","handlingStrategy":"validation","validationCode":"if _, err := mgr.GetProxy(ctx, cfg.GetBaseConfig().Name); err == nil {\n    // exists -> update path, avoid Create conflict\n} else if !errors.Is(err, configmgmt.ErrNotFound) {\n    return err\n}","typeGuard":null,"tryCatchPattern":"if _, err := mgr.CreateProxy(ctx, cfg); err != nil {\n    if errors.Is(err, configmgmt.ErrConflict) {\n        return mgr.UpdateProxy(ctx, cfg.GetBaseConfig().Name, cfg)\n    }\n    return err\n}","preventionTips":["Check-then-create (or upsert) in scripts that run repeatedly","Namespace generated proxy names (e.g. host+service) to avoid collisions","Treat 409 as 'already applied' in idempotent automation"],"tags":["go","frpc","config-management","conflict","admin-api"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}