{"record":{"id":"5ef4a5b22b01fbb2","repo":"XTLS/Xray-core","slug":"failed-to-parse-http-user","errorCode":null,"errorMessage":"failed to parse HTTP user","messagePattern":"failed to parse HTTP user","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"infra/conf/http.go","lineNumber":100,"sourceCode":"\tif len(v.Servers) != 1 {\n\t\treturn nil, errors.New(`HTTP settings: \"servers\" should have one and only one member. Multiple endpoints in \"servers\" should use multiple HTTP outbounds and routing balancer instead`)\n\t}\n\tfor _, serverConfig := range v.Servers {\n\t\tif len(serverConfig.Users) > 1 {\n\t\t\treturn nil, errors.New(`HTTP servers: \"users\" should have one member at most. Multiple members in \"users\" should use multiple HTTP outbounds and routing balancer instead`)\n\t\t}\n\t\tserver := &protocol.ServerEndpoint{\n\t\t\tAddress: serverConfig.Address.Build(),\n\t\t\tPort:    uint32(serverConfig.Port),\n\t\t}\n\t\tfor _, rawUser := range serverConfig.Users {\n\t\t\tuser := new(protocol.User)\n\t\t\tif v.Address != nil {\n\t\t\t\tuser.Level = v.Level\n\t\t\t\tuser.Email = v.Email\n\t\t\t} else {\n\t\t\t\tif err := json.Unmarshal(rawUser, user); err != nil {\n\t\t\t\t\treturn nil, errors.New(\"failed to parse HTTP user\").Base(err).AtError()\n\t\t\t\t}\n\t\t\t}\n\t\t\taccount := new(HTTPAccount)\n\t\t\tif v.Address != nil {\n\t\t\t\taccount.Username = v.Username\n\t\t\t\taccount.Password = v.Password\n\t\t\t} else {\n\t\t\t\tif err := json.Unmarshal(rawUser, account); err != nil {\n\t\t\t\t\treturn nil, errors.New(\"failed to parse HTTP account\").Base(err).AtError()\n\t\t\t\t}\n\t\t\t}\n\t\t\tuser.Account = serial.ToTypedMessage(account.Build())\n\t\t\tserver.User = user\n\t\t\tbreak\n\t\t}\n\t\tconfig.Server = server\n\t\tbreak\n\t}","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/infra/conf/http.go#L82-L118","documentation":"Thrown when json.Unmarshal of a raw entry from httpSettings.servers[].users into protocol.User fails. Each users element is a JSON object whose fields (id/email/level in protocol.User terms) must deserialize cleanly; a type mismatch (e.g. level as a string) or malformed JSON surfaces here with the parse error as the base cause and AtError severity. Only hit on the servers[].users form, not the legacy top-level username form.","triggerScenarios":"A users entry like {\"level\": \"0\"} (string where uint32 expected), an array element that is not an object, or invalid JSON syntax inside the users array.","commonSituations":"Hand-editing JSON and quoting numeric fields; config templating engines rendering booleans or nulls into user objects.","solutions":["Make each users element a valid JSON object with correctly typed fields (e.g. level as number)","Pre-validate the outbound JSON with a linter or jq before feeding it to Xray","Read the chained base error to find the exact JSON offset/field that failed"],"exampleFix":"// before\n\"users\": [ { \"user\": \"a\", \"level\": \"0\" } ]\n// after\n\"users\": [ { \"user\": \"a\", \"level\": 0 } ]","handlingStrategy":"validation","validationCode":"// Validate each users entry unmarshals into protocol.User cleanly\nvar probe protocol.User\nif err := json.Unmarshal(rawUser, &probe); err != nil {\n\treturn fmt.Errorf(\"users entry invalid: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"// Go: catch and enrich\nif err := outboundConf.Build(); err != nil {\n\tif strings.Contains(err.Error(), \"failed to parse HTTP user\") {\n\t\tlog.Printf(\"config file: users[%d] has wrong field types\", i)\n\t}\n\treturn err\n}","preventionTips":["Keep level as a JSON number and email/id as strings","jq-validate the generated config before feeding it to Xray"],"tags":["xray","config","http","json","parsing"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}