YunaiV/yudao-cloud · error · IllegalArgumentException

不支持的设备范围:

Error message

不支持的设备范围:

What it means

Thrown at the end of IotOtaTaskServiceImpl.validateOtaTaskDeviceScope when createReqVO.getDeviceScope() matches neither IotOtaTaskDeviceScopeEnum.SELECT nor IotOtaTaskDeviceScopeEnum.ALL. The method branches on those two known scope values; any other value falls through to `throw new IllegalArgumentException("不支持的设备范围:" + scope)`. Because it is an IllegalArgumentException (not a framework ErrorCodeException), it bypasses the project's GlobalExceptionHandler error-code mapping and surfaces as an unhandled 500.

Source

Thrown at yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java:156

            return devices;
        }
        // 情况二:全部设备
        if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) {
            List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(productId);
            // 2.1.1 移除已经是该固件版本的设备
            devices.removeIf(device -> Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId()));
            // 2.1.2 移除已经在升级中的设备
            List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(
                    convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);
            devices.removeIf(device -> CollUtil.contains(records,
                    item -> item.getDeviceId().equals(device.getId())));
            // 2.2 校验是否有可升级的设备
            if (CollUtil.isEmpty(devices)) {
                throw exception(OTA_TASK_CREATE_FAIL_DEVICE_EMPTY);
            }
            return devices;
        }
        throw new IllegalArgumentException("不支持的设备范围:" + createReqVO.getDeviceScope());
    }

    private IotOtaTaskDO validateUpgradeTaskExists(Long id) {
        IotOtaTaskDO upgradeTask = otaTaskMapper.selectById(id);
        if (Objects.isNull(upgradeTask)) {
            throw exception(OTA_TASK_NOT_EXISTS);
        }
        return upgradeTask;
    }

}

View on GitHub (pinned to 477be9dd49)

Solutions

  1. Check the request body: deviceScope must exactly match IotOtaTaskDeviceScopeEnum.SELECT.getScope() or ALL.getScope() — inspect the enum (yudao-module-iot ... enums/ota/IotOtaTaskDeviceScopeEnum) for the literal int values and send one of those.
  2. If null, make the client always send the field, or add @NotNull on deviceScope in IotOtaTaskCreateReqVO so it fails validation with a clear 400 instead of a 500.
  3. Replace the IllegalArgumentException with a proper ErrorCodeException (add OTA_TASK_DEVICE_SCOPE_NOT_SUPPORTED to the ErrorCodeConstants and throw exception(...)) so clients receive a structured error.
  4. If you intended a new scope (e.g. 'specified product'), extend IotOtaTaskDeviceScopeEnum and add a matching branch in validateOtaTaskDeviceScope before the fall-through.

Example fix

// before
throw new IllegalArgumentException("不支持的设备范围:" + createReqVO.getDeviceScope());

// after (structured error code)
throw exception(OTA_TASK_DEVICE_SCOPE_NOT_SUPPORTED, createReqVO.getDeviceScope());

// and on the request VO, reject null early
// @NotNull(message = "设备范围不能为空")
// private Integer deviceScope;
Defensive patterns

Strategy: validation

Validate before calling

import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;

boolean validScope = Arrays.stream(IotOtaTaskDeviceScopeEnum.values())
        .map(IotOtaTaskDeviceScopeEnum::getScope)
        .anyMatch(s -> Objects.equals(s, createReqVO.getDeviceScope()));
if (!validScope) {
    // reject before the service call with a clear message
    throw new IllegalArgumentException("deviceScope must be one of "
            + Arrays.toString(IotOtaTaskDeviceScopeEnum.values()));
}

Type guard

private static final Set<Integer> SUPPORTED_DEVICE_SCOPES = Arrays.stream(IotOtaTaskDeviceScopeEnum.values())
        .map(IotOtaTaskDeviceScopeEnum::getScope)
        .collect(Collectors.toSet());

static boolean isSupportedDeviceScope(Integer scope) {
    return scope != null && SUPPORTED_DEVICE_SCOPES.contains(scope);
}

Try / catch

// Prefer validation over catching: IllegalArgumentException here is a 500-level leak.
// If forced to catch (legacy caller):
try {
    otaTaskService.createOtaTask(createReqVO);
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("不支持的设备范围")) {
        // client sent a bad deviceScope: fix the request payload
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling createOtaTask / the POST create endpoint with deviceScope set to a value outside {SELECT scope (typically 1), ALL scope (typically 2)} — e.g. 3, 0, a random int, or null (null fails both Objects.equals checks and falls through). It is thrown before any device list is built, right after the firmware/product validation.

Common situations: Frontend sends a new scope value (or a stale enum int) after a version change; hand-written API calls (curl/Postman) with a guessed deviceScope; null deviceScope from a form that never set the field; serialization mismatch (string "1" vs int 1) so the equality check fails.

Related errors


AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14). Data as JSON: /api/errors/1d5d499dc90b144c. Report an issue: GitHub.