dromara/Sa-Token · warning · RuntimeException
此设备id目前未绑定任何用户
Error message
此设备id目前未绑定任何用户
What it means
Demo utility in sa-token-demo-device-lock: getUserIdByDeviceId looks up the persisted deviceId -> userId mapping in SaTokenDao under key '<tokenName>:device-to-userid:<deviceId>'. A null result (key absent or expired) throws RuntimeException('此设备id目前未绑定任何用户'). Because the mapping is written with a 1200-second (20-minute) TTL, the binding silently disappears after 20 minutes or a Redis flush.
Source
Thrown at sa-token-demo/sa-token-demo-device-lock/src/main/java/com/pj/util/DeviceLockCheckUtil.java:32
* 保存设备id与用户id的映射关系
* @param deviceId /
* @param userId /
*/
public static void setDeviceIdToUserId(String deviceId, long userId) {
if(SaFoxUtil.isEmpty(deviceId) || SaFoxUtil.isEmpty(userId)) {
throw new RuntimeException("设备id或用户id不能为空");
}
SaManager.getSaTokenDao().set(saveKeyPrefix() + deviceId, String.valueOf(userId), 1200);
}
/**
* 返回设备id绑定的用户id
* @param deviceId /
*/
public static long getUserIdByDeviceId(String deviceId) {
String userIdStr = SaManager.getSaTokenDao().get(saveKeyPrefix() + deviceId);
if(userIdStr == null) {
throw new RuntimeException("此设备id目前未绑定任何用户");
}
return Long.parseLong(userIdStr);
}
// 返回数据保存时使用的前缀
public static Object saveKeyPrefix() {
return SaManager.getConfig().getTokenName() + ":device-to-userid:";
}
}View on GitHub (pinned to ac2c7f6e94)
Solutions
- Re-bind before lookup: call setDeviceIdToUserId(deviceId, userId) at login, then getUserIdByDeviceId on subsequent requests
- Handle the exception (or a null check) as 'device not bound' and route the user to re-login/re-bind
- If longer bindings are needed, raise the 1200-second TTL in setDeviceIdToUserId or refresh it on each successful check
Example fix
// before
long userId = DeviceLockCheckUtil.getUserIdByDeviceId(deviceId); // throws after TTL
// after
try {
long userId = DeviceLockCheckUtil.getUserIdByDeviceId(deviceId);
} catch (RuntimeException e) {
// binding expired: force re-login / re-bind device
} Defensive patterns
Strategy: try-catch
Validate before calling
String v = SaManager.getSaTokenDao().get(
DeviceLockCheckUtil.saveKeyPrefix() + deviceId);
if (v == null) {
// binding missing/expired -> ask user to re-login, no exception needed
} Try / catch
try {
long userId = DeviceLockCheckUtil.getUserIdByDeviceId(deviceId);
} catch (RuntimeException e) {
if (e.getMessage().contains("未绑定")) {
// treat as 'device not bound': redirect to re-bind flow
}
} Prevention
- Remember the 1200s TTL: refresh the binding on each successful request or raise it
- Re-bind devices at every login
- Check the dao key directly instead of relying on the exception if you need control flow without exceptions
When it happens
Trigger: Calling getUserIdByDeviceId with a deviceId never registered, a deviceId whose binding expired (TTL 1200s since setDeviceIdToUserId), or after the underlying SaTokenDao was cleared/restarted (in-memory dao lost on reboot).
Common situations: User returning after >20 minutes on a bound device; server restart wiping the default in-memory dao; typo'd or rotated device id from the client; Redis eviction under memory pressure.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/833dc1b043cb9cf9.
Report an issue: GitHub.