{"record":{"id":"af6c8afc01246c9e","repo":"fatedier/frp","slug":"erralreadyexists","errorCode":"ErrAlreadyExists","errorMessage":"already exists","messagePattern":"already exists","errorType":"error_code","errorClass":null,"httpStatus":409,"severity":"error","filePath":"pkg/config/source/store.go","lineNumber":42,"sourceCode":"\t\"github.com/fatedier/frp/pkg/util/jsonx\"\n)\n\ntype StoreSourceConfig struct {\n\tPath string `json:\"path\"`\n}\n\ntype storeData struct {\n\tProxies  []v1.TypedProxyConfig   `json:\"proxies,omitempty\"`\n\tVisitors []v1.TypedVisitorConfig `json:\"visitors,omitempty\"`\n}\n\ntype StoreSource struct {\n\tbaseSource\n\tconfig StoreSourceConfig\n}\n\nvar (\n\tErrAlreadyExists = errors.New(\"already exists\")\n\tErrNotFound      = errors.New(\"not found\")\n)\n\nconst (\n\tstoreKindProxy   = \"proxy\"\n\tstoreKindVisitor = \"visitor\"\n)\n\nfunc NewStoreSource(cfg StoreSourceConfig) (*StoreSource, error) {\n\tif cfg.Path == \"\" {\n\t\treturn nil, fmt.Errorf(\"path is required\")\n\t}\n\n\ts := &StoreSource{\n\t\tbaseSource: newBaseSource(),\n\t\tconfig:     cfg,\n\t}\n","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/source/store.go#L24-L60","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nerr := storeSrc.Create(cfg) // already exists\n\n// after\nif _, err := storeSrc.Get(kind, name); errors.Is(err, source.ErrNotFound) {\n    err = storeSrc.Create(cfg)\n} else if err == nil {\n    err = storeSrc.Update(name, cfg) // or skip\n}","handlingStrategy":"validation","validationCode":"if _, err := storeSrc.Get(kind, name); err == nil {\n    return fmt.Errorf(\"%q already exists; use update\", name)\n} else if !errors.Is(err, source.ErrNotFound) {\n    return err\n}","typeGuard":null,"tryCatchPattern":"err := storeSrc.Create(cfg)\nif errors.Is(err, source.ErrAlreadyExists) {\n    return storeSrc.Update(name, cfg) // or no-op if idempotent\n}","preventionTips":["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"],"tags":["go","config","store","conflict","frpc"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}