Skip to content

Changing user UUID is not applied to xray hot sync: AddUser fails with "already exists", old UUID remains active #53

@Pimppp

Description

@Pimppp

Summary

When a user's UUID is changed in the Xboard panel, xboard-node does not correctly update the user inside the running xray instance via hot sync.

Instead, xboard-node appears to call UserManager.AddUser for the same user email. xray rejects this operation with:

proxy/vless: User user@2942 already exists.

After that, xboard-node still logs the update as successful:

users updated via UserManager added=1 removed=0 total=12
users updated: +1 -0

However, the new UUID from the panel is rejected by xray, while the old UUID appears to remain active until the node/xray process is restarted.

Environment

  • xboard-node version: v1.13
  • build time: 2026-04-19T20:10:46Z
  • xbctl version: v1.13
  • xboard-node status: latest, active, health ok
  • OS: Debian GNU/Linux 12 bookworm
  • Kernel: Linux 6.1.0-42-amd64
  • Deployment mode: machine
  • Runtime: systemd
  • machine_id: 3
  • Global configured kernel: singbox
  • xray version from logs: Xray 26.3.27

Machine mode details

The machine discovers two VLESS nodes:

machine 3: discovered 2 nodes
machine: starting node 29 (vless/🇱🇺Netherlands (Fast))
machine: starting node 42 (vless/🇱🇺Netherlands)

The services are:

[vless:4430] started, 12 users
[vless:8080] started, 12 users

The global config uses sing-box:

kernel:
  type: singbox

But xboard-node automatically switches the XHTTP node to xray:

machine: auto-switching kernel for node 42 (singbox→xray, transport=xhttp)

Node 42 config excerpt:

{
  "protocol": "vless",
  "listen_ip": "0.0.0.0",
  "server_port": 8080,
  "network": "xhttp",
  "tls": 1,
  "tls_settings": {
    "server_name": "example.com",
    "allow_insecure": false
  },
  "base_config": {
    "push_interval": 60,
    "pull_interval": 60
  }
}

Node 29 config excerpt:

{
  "protocol": "vless",
  "listen_ip": "0.0.0.0",
  "server_port": 4430,
  "network": "tcp",
  "tls": 1,
  "flow": "xtls-rprx-vision",
  "tls_settings": {
    "server_name": "example.com",
    "allow_insecure": false
  },
  "base_config": {
    "push_interval": 60,
    "pull_interval": 60
  }
}

What happened

User 2942 had their UUID changed in the panel.
The panel API currently returns user 2942 with the new UUID:

{
  "id": 2942,
  "uuid": "3e4ce6e5-2d8d-4307-895f-c9a1a348cddd",
  "speed_limit": null,
  "device_limit": 5
}

Full user list excerpt from the panel API for the VLESS node:

{
  "users": [
    {"id":1,"uuid":"REDACTED","speed_limit":1000,"device_limit":5},
    {"id":5,"uuid":"REDACTED","speed_limit":1000,"device_limit":5},
    {"id":22,"uuid":"REDACTED","speed_limit":1000,"device_limit":5},
    {"id":35,"uuid":"REDACTED","speed_limit":1000,"device_limit":5},
    {"id":2917,"uuid":"REDACTED","speed_limit":1000,"device_limit":5},
    {"id":2922,"uuid":"REDACTED","speed_limit":1000,"device_limit":5},
    {"id":2927,"uuid":"REDACTED","speed_limit":1000,"device_limit":5},
    {"id":2929,"uuid":"REDACTED","speed_limit":1000,"device_limit":5},
    {"id":2937,"uuid":"REDACTED","speed_limit":null,"device_limit":5},
    {"id":2940,"uuid":"REDACTED","speed_limit":null,"device_limit":5},
    {"id":2941,"uuid":"REDACTED","speed_limit":1000,"device_limit":null},
    {"id":2942,"uuid":"3e4ce6e5-2d8d-4307-895f-c9a1a348cddd","speed_limit":null,"device_limit":5}
  ]
}

But during hot sync, xboard-node/xray logs:

06:57:55.406 WARN [core] xray: AddUser failed in UpdateUsers user=2942
error=proxy/vless: User user@2942 already exists.

Immediately after that, xboard-node still reports the update as successful:

06:57:55.406 INFO [core] xray: users updated via UserManager added=1 removed=0 total=12
06:57:55.406 INFO [vless:8080] users updated: +1 -0

At the same time, connections with the new UUID from the panel are rejected:

proxy/vless/encoding: invalid request user id: 3e4ce6e5-2d8d-4307-895f-c9a1a348cddd

Before/around the failed update, xray still accepted user@2942:

06:57:35.422525 accepted tcp:captive.apple.com:80 [vless-in >> direct] email: user@2942

This suggests that xray still had the previous runtime entry for user@2942, while the new UUID from the panel was not applied.

Expected behavior

When a user's UUID changes in the panel, xboard-node should update the running xray state correctly.
Possible expected behavior:

  1. Detect that the user already exists by email but has changed UUID.
  2. Remove the old user from xray.
  3. Add the same user email with the new UUID.
  4. Or restart/reload the affected inbound if xray does not support in-place UUID replacement.
  5. Do not log added=1 / users updated: +1 -0 if AddUser failed.
    The new UUID from the panel should become valid after hot sync without requiring a full node restart.

Actual behavior

The new UUID from the panel is rejected by xray:

invalid request user id: 3e4ce6e5-2d8d-4307-895f-c9a1a348cddd

The old runtime user entry appears to remain active.

AddUser fails because the same email already exists:

User user@2942 already exists.

But xboard-node still logs:

users updated via UserManager added=1 removed=0 total=12
users updated: +1 -0

This makes the sync look successful even though the new UUID was not applied.

Why this is critical

Changing a user's UUID in the panel is a normal administrative action, for example when regenerating a user's subscription/profile.
Currently, after changing UUID:

  • the panel returns the new UUID;
  • clients receive/use the new UUID;
  • xray still has the old runtime user entry;
  • the new UUID is rejected;
  • xboard-node logs the update as successful;
  • the issue is fixed only after restarting the node/xray process.
    This makes hot user sync unreliable and hard to debug in production.

Suggested fix

When AddUser returns already exists, xboard-node should not count it as a successful add.

If the panel UUID for the same user email changed, xboard-node should remove the old runtime user and add the new one, or reload the affected inbound/kernel if xray does not support safe in-place replacement.

At minimum, the final counters should not report added=1 when AddUser failed.

Additional note

The most confusing part is the misleading success log after a failed AddUser call:

AddUser failed in UpdateUsers user=2942
users updated via UserManager added=1 removed=0 total=12
users updated: +1 -0

Even if the replacement logic is not implemented yet, the counters/logs should reflect the real result.

I also attached a redacted log file for the relevant time window.
Sensitive domains, tokens and IP addresses were redacted.

xboard-node-uuid-hot-sync-bug.log

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions