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:
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:
- Detect that the user already exists by email but has changed UUID.
- Remove the old user from xray.
- Add the same user email with the new UUID.
- Or restart/reload the affected inbound if xray does not support in-place UUID replacement.
- 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
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.AddUserfor the same user email. xray rejects this operation with:After that, xboard-node still logs the update as successful:
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
Machine mode details
The machine discovers two VLESS nodes:
The services are:
The global config uses sing-box:
But xboard-node automatically switches the XHTTP node to xray:
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:
Immediately after that, xboard-node still reports the update as successful:
At the same time, connections with the new UUID from the panel are rejected:
Before/around the failed update, xray still accepted 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:
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:
The old runtime user entry appears to remain active.
AddUser fails because the same email already exists:
But xboard-node still logs:
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:
This makes hot user sync unreliable and hard to debug in production.
Suggested fix
When
AddUserreturnsalready 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=1whenAddUserfailed.Additional note
The most confusing part is the misleading success log after a failed AddUser call:
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