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

  1. Re-bind before lookup: call setDeviceIdToUserId(deviceId, userId) at login, then getUserIdByDeviceId on subsequent requests
  2. Handle the exception (or a null check) as 'device not bound' and route the user to re-login/re-bind
  3. 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

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.