{"record":{"id":"6d7f658df9387d76","repo":"AdguardTeam/AdGuardHome","slug":"another-client-q-uses-the-same-clientid-q","errorCode":null,"errorMessage":"another client %q uses the same ClientID %q","messagePattern":"another client %q uses the same ClientID %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/index.go","lineNumber":117,"sourceCode":"\t\treturn fmt.Errorf(\"another client %q uses the same uid\", p.Name)\n\t}\n\n\treturn nil\n}\n\n// clashes returns an error if the index contains a different persistent client\n// with at least a single identifier contained by c.  c must be non-nil.\nfunc (ci *index) clashes(c *Persistent) (err error) {\n\tif p := ci.clashesName(c); p != nil {\n\t\treturn fmt.Errorf(\"another client uses the same name %q\", p.Name)\n\t}\n\n\tfor _, id := range c.ClientIDs {\n\t\texisting, ok := ci.clientIDToUID[id]\n\t\tif ok && existing != c.UID {\n\t\t\tp := ci.uidToClient[existing]\n\n\t\t\treturn fmt.Errorf(\"another client %q uses the same ClientID %q\", p.Name, id)\n\t\t}\n\t}\n\n\tp, ip := ci.clashesIP(c)\n\tif p != nil {\n\t\treturn fmt.Errorf(\"another client %q uses the same IP %q\", p.Name, ip)\n\t}\n\n\tp, s := ci.clashesSubnet(c)\n\tif p != nil {\n\t\treturn fmt.Errorf(\"another client %q uses the same subnet %q\", p.Name, s)\n\t}\n\n\tp, mac := ci.clashesMAC(c)\n\tif p != nil {\n\t\treturn fmt.Errorf(\"another client %q uses the same MAC %q\", p.Name, mac)\n\t}\n","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/client/index.go#L99-L135","documentation":"Thrown when adding or updating a persistent client whose ClientID collides with a different client already in the storage index. The index maps each ClientID to a unique UID, so two distinct clients cannot share a ClientID. The error names the conflicting client and the duplicated ID.","triggerScenarios":"Calling Storage.Add or Storage.Update (or the internal index clashes check) with a Client whose ClientIDs slice contains an ID already registered under another client's UID (i.e. a different Name/UID).","commonSituations":"Migrating or importing client lists that contain duplicate IDs (e.g. the same TLS CN or username appearing twice), copying a client configuration and forgetting to change its IDs, or UI edits that merge two clients into overlapping ID sets.","solutions":["Inspect the stored clients and find the client named in the error message; remove the duplicated ID from one of the two clients","If the two entries should be the same client, update the existing one instead of adding a new one","Deduplicate ClientIDs across the whole initial client list before passing it to NewStorage"],"exampleFix":"// before\nclient.ClientIDs = []string{\"alice\"} // already used by client \"laptop\"\n_ = s.Add(ctx, client)\n\n// after\nclient.ClientIDs = []string{\"alice-laptop\"}\n_ = s.Add(ctx, client)","handlingStrategy":"validation","validationCode":"func hasClientIDClash(persistent []client.Persistent, c client.Persistent) bool {\n    for _, p := range persistent {\n        if p.UID == c.UID { continue }\n        for _, id := range c.ClientIDs {\n            if slices.Contains(p.ClientIDs, id) { return true }\n        }\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":"err := s.Add(ctx, c)\nif err != nil && strings.Contains(err.Error(), \"uses the same ClientID\") {\n    // resolve conflict or fall back to Update on existing client\n}","preventionTips":["Maintain a set of used ClientIDs and check before every Add/Update","Treat imports as upserts: match on existing IDs before inserting"],"tags":["client","duplicate","clientid","validation"],"backgroundTag":"duplicate-identifier-conflict","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}