{"record":{"id":"524e924c2eec28bc","repo":"XTLS/Xray-core","slug":"user-email-not-found-524e92","errorCode":null,"errorMessage":"User ${email} not found.","messagePattern":"User (.+?) not found\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/trojan/validator.go","lineNumber":38,"sourceCode":"\tif u.Email != \"\" {\n\t\t_, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u)\n\t\tif loaded {\n\t\t\treturn errors.New(\"User \", u.Email, \" already exists.\")\n\t\t}\n\t}\n\tv.users.Store(hexString(u.Account.(*MemoryAccount).Key), u)\n\treturn nil\n}\n\n// Del a trojan user with a non-empty Email.\nfunc (v *Validator) Del(e string) error {\n\tif e == \"\" {\n\t\treturn errors.New(\"Email must not be empty.\")\n\t}\n\tle := strings.ToLower(e)\n\tu, _ := v.email.Load(le)\n\tif u == nil {\n\t\treturn errors.New(\"User \", e, \" not found.\")\n\t}\n\tv.email.Delete(le)\n\tv.users.Delete(hexString(u.(*protocol.MemoryUser).Account.(*MemoryAccount).Key))\n\treturn nil\n}\n\n// Get a trojan user with hashed key, nil if user doesn't exist.\nfunc (v *Validator) Get(hash string) *protocol.MemoryUser {\n\tu, _ := v.users.Load(hash)\n\tif u != nil {\n\t\treturn u.(*protocol.MemoryUser)\n\t}\n\treturn nil\n}\n\n// Get a trojan user with hashed key, nil if user doesn't exist.\nfunc (v *Validator) GetByEmail(email string) *protocol.MemoryUser {\n\temail = strings.ToLower(email)","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/trojan/validator.go#L20-L56","documentation":"Validator.Del found no user registered under the given (lowercased) email — v.email.Load returned nil. Nothing was deleted; the error tells you the key does not match any current user of this trojan inbound.","triggerScenarios":"Deleting an email that was never added, was already deleted (double-remove), or differs in more than case (typo, trailing whitespace, domain change).","commonSituations":"Idempotent-retry scripts hitting 'already removed' users; stale config drift between what operators think is registered and actual state; emails edited on the client side only.","solutions":["List the inbound's current users (API/config) and use the email exactly as stored","Make deletion flows tolerate 'not found' as success if idempotency is desired","Check for whitespace/case drift in the email string before calling Del"],"exampleFix":"// tolerate double-delete in automation\nif err := validator.Del(email); err != nil && !strings.Contains(err.Error(), \"not found\") {\n    return err\n}\nreturn nil","handlingStrategy":"try-catch","validationCode":"// check existence first\nfunc userExists(v *Validator, email string) bool {\n    u, _ := v.email.Load(strings.ToLower(email))\n    return u != nil\n}","typeGuard":null,"tryCatchPattern":"if err := v.Del(email); err != nil {\n    if strings.Contains(err.Error(), \"not found\") {\n        return nil // deletion is idempotent\n    }\n    return err\n}","preventionTips":["Make remove flows idempotent by treating 'not found' as success","Normalize emails (trim + lowercase) before any Add/Del call","Maintain one source of truth for the user list instead of editing config and API state separately"],"tags":["trojan","users","api-misuse"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}