XTLS/Xray-core · error

invalid VMess user

Error message

invalid VMess user

What it means

Thrown by VMessInboundConfig.Build() (processUser closure) when a raw entry of the inbound "clients"/"users" array fails to unmarshal into protocol.User. The first decode pass expects the common user envelope: "email" as string and "level" as a JSON number. A type mismatch or malformed JSON fragment makes json.Unmarshal fail and this error wraps the cause.

Source

Thrown at infra/conf/vmess.go:81

// Build implements Buildable
func (c *VMessInboundConfig) Build() (proto.Message, error) {
	errors.PrintNonRemovalDeprecatedFeatureWarning("VMess (with no Forward Secrecy, etc.)", "VLESS Encryption")

	config := &inbound.Config{}

	if c.Defaults != nil {
		config.Default = c.Defaults.Build()
	}

	if c.Clients != nil {
		c.Users = c.Clients
	}
	config.User = make([]*protocol.User, len(c.Users))
	processUser := func(idx int) error {
		rawData := c.Users[idx]
		user := new(protocol.User)
		if err := json.Unmarshal(rawData, user); err != nil {
			return errors.New("invalid VMess user").Base(err)
		}
		account := new(VMessAccount)
		if err := json.Unmarshal(rawData, account); err != nil {
			return errors.New("invalid VMess user").Base(err)
		}

		u, err := uuid.ParseString(account.ID)
		if err != nil {
			return err
		}
		account.ID = u.String()

		user.Account = serial.ToTypedMessage(account.Build())
		config.User[idx] = user
		return nil
	}
	if err := task.ParallelForN(len(c.Users), processUser); err != nil {
		return nil, err

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Read the .Base(err) json cause — it names the exact field and expected type
  2. Ensure level is an unquoted number and email a string
  3. Run the config through a JSON validator (jq, jsonlint) before starting Xray

Example fix

// before
"clients": [ { "id": "...", "level": "0", "email": "u" } ]
// after
"clients": [ { "id": "...", "level": 0, "email": "u" } ]
Defensive patterns

Strategy: validation

Validate before calling

func validateVMessClients(cfg map[string]any) error {
	inbounds, _ := cfg["inbounds"].([]any)
	for _, ib := range inbounds {
		m, _ := ib.(map[string]any)
		if p, _ := m["protocol"].(string); p != "vmess" { continue }
		settings, _ := m["settings"].(map[string]any)
		clients, _ := settings["clients"].([]any)
		for i, c := range clients {
			cm, _ := c.(map[string]any)
			if l, ok := cm["level"]; ok {
				if _, isNum := l.(float64); !isNum {
					return fmt.Errorf("inbound %v: clients[%d].level must be a number", m["tag"], i)
				}
			}
			if e, ok := cm["email"]; ok {
				if _, isStr := e.(string); !isStr {
					return fmt.Errorf("inbound %v: clients[%d].email must be a string", m["tag"], i)
				}
			}
		}
	}
	return nil
}

Type guard

func vmessClientEnvelopeOK(raw json.RawMessage) bool {
	var user protocol.User
	return json.Unmarshal(raw, &user) == nil
}

Prevention

When it happens

Trigger: "clients":[{"email":"a@b","level":"1"}] — level quoted; broken JSON in one client object; a nested object where a scalar is expected. Note c.Clients is aliased into c.Users before processing, so both keys are validated identically.

Common situations: YAML frontends or templates quoting all values; hand-editing client lists; importing client arrays from panels that emit stringly-typed levels.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/b4a8dccd24d32eb5. Report an issue: GitHub.