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 = serviceView on GitHub (pinned to 7d214f8b09)
Solutions
- 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).
- Do not put per-user keys in the inbound-level field; those go in users[].
- 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
- Remember field naming: single-user inbound uses `password`, multi-user uses `key`.
- Template configs with the key field pre-marked required.
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.