{"record":{"id":"1d5d499dc90b144c","repo":"YunaiV/yudao-cloud","slug":"error-1d5d49","errorCode":null,"errorMessage":"不支持的设备范围：","messagePattern":"不支持的设备范围：","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":500,"severity":"error","filePath":"yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java","lineNumber":156,"sourceCode":"            return devices;\n        }\n        // 情况二：全部设备\n        if (Objects.equals(createReqVO.getDeviceScope(), IotOtaTaskDeviceScopeEnum.ALL.getScope())) {\n            List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(productId);\n            // 2.1.1 移除已经是该固件版本的设备\n            devices.removeIf(device -> Objects.equals(device.getFirmwareId(), createReqVO.getFirmwareId()));\n            // 2.1.2 移除已经在升级中的设备\n            List<IotOtaTaskRecordDO> records = otaTaskRecordService.getOtaTaskRecordListByDeviceIdAndStatus(\n                    convertSet(devices, IotDeviceDO::getId), IotOtaTaskRecordStatusEnum.IN_PROCESS_STATUSES);\n            devices.removeIf(device -> CollUtil.contains(records,\n                    item -> item.getDeviceId().equals(device.getId())));\n            // 2.2 校验是否有可升级的设备\n            if (CollUtil.isEmpty(devices)) {\n                throw exception(OTA_TASK_CREATE_FAIL_DEVICE_EMPTY);\n            }\n            return devices;\n        }\n        throw new IllegalArgumentException(\"不支持的设备范围：\" + createReqVO.getDeviceScope());\n    }\n\n    private IotOtaTaskDO validateUpgradeTaskExists(Long id) {\n        IotOtaTaskDO upgradeTask = otaTaskMapper.selectById(id);\n        if (Objects.isNull(upgradeTask)) {\n            throw exception(OTA_TASK_NOT_EXISTS);\n        }\n        return upgradeTask;\n    }\n\n}\n","sourceCodeStart":138,"sourceCodeEnd":168,"githubUrl":"https://github.com/YunaiV/yudao-cloud/blob/477be9dd49ab7223a972a6abdff0684d6423dec3/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskServiceImpl.java#L138-L168","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","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.","If you intended a new scope (e.g. 'specified product'), extend IotOtaTaskDeviceScopeEnum and add a matching branch in validateOtaTaskDeviceScope before the fall-through."],"exampleFix":"// before\nthrow new IllegalArgumentException(\"不支持的设备范围：\" + createReqVO.getDeviceScope());\n\n// after (structured error code)\nthrow exception(OTA_TASK_DEVICE_SCOPE_NOT_SUPPORTED, createReqVO.getDeviceScope());\n\n// and on the request VO, reject null early\n// @NotNull(message = \"设备范围不能为空\")\n// private Integer deviceScope;","handlingStrategy":"validation","validationCode":"import cn.iocoder.yudao.module.iot.enums.ota.IotOtaTaskDeviceScopeEnum;\n\nboolean validScope = Arrays.stream(IotOtaTaskDeviceScopeEnum.values())\n        .map(IotOtaTaskDeviceScopeEnum::getScope)\n        .anyMatch(s -> Objects.equals(s, createReqVO.getDeviceScope()));\nif (!validScope) {\n    // reject before the service call with a clear message\n    throw new IllegalArgumentException(\"deviceScope must be one of \"\n            + Arrays.toString(IotOtaTaskDeviceScopeEnum.values()));\n}","typeGuard":"private static final Set<Integer> SUPPORTED_DEVICE_SCOPES = Arrays.stream(IotOtaTaskDeviceScopeEnum.values())\n        .map(IotOtaTaskDeviceScopeEnum::getScope)\n        .collect(Collectors.toSet());\n\nstatic boolean isSupportedDeviceScope(Integer scope) {\n    return scope != null && SUPPORTED_DEVICE_SCOPES.contains(scope);\n}","tryCatchPattern":"// Prefer validation over catching: IllegalArgumentException here is a 500-level leak.\n// If forced to catch (legacy caller):\ntry {\n    otaTaskService.createOtaTask(createReqVO);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"不支持的设备范围\")) {\n        // client sent a bad deviceScope: fix the request payload\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always populate deviceScope from IotOtaTaskDeviceScopeEnum on the client; never hardcode the int.","Add @NotNull to deviceScope in the VO so nulls fail as 400 validation errors.","Keep frontend enum constants in sync with backend IotOtaTaskDeviceScopeEnum across version upgrades.","Test createOtaTask with all enum values plus an invalid one in contract tests to catch drift early."],"tags":["iot","ota","task","enum","validation","illegal-argument","java","api-contract"],"backgroundTag":null,"analyzedSha":"477be9dd49ab7223a972a6abdff0684d6423dec3","analyzedAt":"2026-08-14T13:35:31.121Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}