MHSanaei/3x-ui · error
invalid type for user field %q: %T
Error message
invalid type for user field %q: %T
What it means
The required-field variant of the type check in getRequiredUserString: the key exists and is non-nil, but its value is not a Go string (e.g. a float64 from JSON numbers, a bool, or a nested map). The %T verb in the message tells you the concrete type that arrived, which pinpoints the serialization mistake upstream.
Source
Thrown at internal/xray/api.go:68
// XrayAPI is a gRPC client for managing Xray core configuration, inbounds, outbounds, and statistics.
type XrayAPI struct {
HandlerServiceClient *command.HandlerServiceClient
StatsServiceClient *statsService.StatsServiceClient
RoutingServiceClient *routerService.RoutingServiceClient
grpcClient *grpc.ClientConn
isConnected bool
StatsLastValues map[string]int64
}
func getRequiredUserString(user map[string]any, key string) (string, error) {
value, ok := user[key]
if !ok || value == nil {
return "", fmt.Errorf("missing required user field %q", key)
}
strValue, ok := value.(string)
if !ok {
return "", fmt.Errorf("invalid type for user field %q: %T", key, value)
}
return strValue, nil
}
func getOptionalUserString(user map[string]any, key string) (string, error) {
value, ok := user[key]
if !ok || value == nil {
return "", nil
}
strValue, ok := value.(string)
if !ok {
return "", fmt.Errorf("invalid type for user field %q: %T", key, value)
}
return strValue, nil
}View on GitHub (pinned to ad32144c42)
Solutions
- Match the %T in the message to the offending field and fix the producer: ensure the JSON value is a string (quote numbers/booleans where a string is expected).
- Correct the stored client settings via the panel editor (re-enter the field) so the regenerated map has the right type.
- Add upstream schema validation (Zod/JSON schema on the inbound settings) so type-wrong clients are rejected at the API boundary with a precise path instead of deep inside Xray user building.
Example fix
// before
user := map[string]any{"email": 12345}
// after
user := map[string]any{"email": "12345"} Defensive patterns
Strategy: type-guard
Validate before calling
// Coerce/validate string fields before passing the map to the Xray builder
for k, v := range user {
if s, ok := v.(string); ok {
user[k] = strings.TrimSpace(s)
}
} Type guard
func allUserStrings(user map[string]any, keys ...string) error {
for _, k := range keys {
if v, ok := user[k]; ok && v != nil {
if _, isStr := v.(string); !isStr {
return fmt.Errorf("field %q must be a string, got %T", k, v)
}
}
}
return nil
} Try / catch
if strings.Contains(err.Error(), "invalid type for user field") {
// %T in message names the actual type; fix producer serialization, no retry
} Prevention
- Always marshal client settings from typed structs (or Zod-validated JSON), never from loosely-typed maps.
- Quote numeric-looking identifiers (IDs, keepAlive) in client JSON.
When it happens
Trigger: Building a user map from JSON where a string field was encoded as a number or boolean — e.g. "email": 123, or "publicKey": {"b64": "..."} — and passing it into the Xray account builder that calls getRequiredUserString.
Common situations: Generated/imported client JSON from external tools with wrong types; a frontend or API consumer sending form values that get coerced to numbers; refactors that changed a field's Go type while stale clients still hold the old shape in the DB.
Related errors
- local xray is not running
- xray is not running
- xray is already running
- xray is not running
- invalid metric (unknown metric)
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/59149ed79ba993a5.
Report an issue: GitHub.