XTLS/Xray-core · error

missing key

Error message

missing key

What it means

Hard guard in the multi-user 2022 inbound constructor: the server-level pre-shared key field (config.Key) is an empty string. Shadowsocks-2022 mandates a server PSK from which per-user identity keys are derived, so an empty key is rejected before base64 decoding or service creation. Startup fails immediately.

Source

Thrown at proxy/shadowsocks_2022/inbound_multi.go:72

	memUsers := []*protocol.MemoryUser{}
	for i, user := range config.Users {
		if user.Email == "" {
			u := uuid.New()
			user.Email = "unnamed-user-" + strconv.Itoa(i) + "-" + u.String()
		}
		u, err := user.ToMemoryUser()
		if err != nil {
			return nil, errors.New("failed to get shadowsocks user").Base(err).AtError()
		}
		memUsers = append(memUsers, u)
	}

	inbound := &MultiUserInbound{
		networks: networks,
		users:    memUsers,
	}
	if config.Key == "" {
		return nil, errors.New("missing key")
	}
	psk, err := base64.StdEncoding.DecodeString(config.Key)
	if err != nil {
		return nil, errors.New("parse config").Base(err)
	}
	service, err := shadowaead_2022.NewMultiService[int](config.Method, psk, 500, inbound, nil)
	if err != nil {
		return nil, errors.New("create service").Base(err)
	}
	err = service.UpdateUsersWithPasswords(
		C.MapIndexed(memUsers, func(index int, it *protocol.MemoryUser) int { return index }),
		C.Map(memUsers, func(it *protocol.MemoryUser) string { return it.Account.(*MemoryAccount).Key }),
	)
	if err != nil {
		return nil, errors.New("create service").Base(err)
	}

	inbound.service = service

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Set the inbound-level `key` to a base64 PSK of the length required by the method (32 bytes for 256-bit, 16 for 128-bit).
  2. Do not put per-user keys in the inbound-level field; those go in users[].
  3. Re-run xray run -test to confirm.

Example fix

// before
{ "protocol": "shadowsocks-2022", "settings": { "method": "2022-blake3-aes-256-gcm", "key": "" } }
// after
{ "protocol": "shadowsocks-2022", "settings": { "method": "2022-blake3-aes-256-gcm", "key": "vJ+Q7vP4n3...==" } }
Defensive patterns

Strategy: validation

Validate before calling

func validateSS2022Inbound(cfg *MultiInboundConfig) error {
  if strings.TrimSpace(cfg.Key) == "" {
    return errors.New("shadowsocks-2022 multi inbound requires a server-level base64 `key`")
  }
  return nil
}

Type guard

func hasServerKey(key string) bool { return strings.TrimSpace(key) != "" }

Prevention

When it happens

Trigger: Creating a shadowsocks-2022 multi-user inbound whose JSON omits `key` or sets it to "" — e.g. config generated by copying the single-user template where the field is named `password`, not `key`.

Common situations: Confusion between single-user inbound (`password` field) and multi-user inbound (`key` field); panel not emitting the key field; hand-written config missing the line.

Related errors


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