{"record":{"id":"2515ae98cd5dac52","repo":"pinpoint-apm/pinpoint","slug":"there-is-not-ruleid-to-delete-alarm-rule","errorCode":null,"errorMessage":"there is not ruleId to delete alarm rule","messagePattern":"there is not ruleId to delete alarm rule","errorType":"http","errorClass":"ResponseStatusException","httpStatus":400,"severity":"warning","filePath":"web/src/main/java/com/navercorp/pinpoint/web/authorization/controller/AlarmController.java","lineNumber":77,"sourceCode":"    public AlarmController(AlarmService alarmService) {\n        this.alarmService = Objects.requireNonNull(alarmService, \"alarmService\");\n    }\n\n    @PreAuthorize(\"@naverPermissionEvaluator.hasAlarmPermission(#serviceName.getName(), #rule.getApplicationName(), T(com.navercorp.pinpoint.web.security.PermissionChecker).PERMISSION_ALARM_EDIT_ALARM_ONLY_MANAGER)\")\n    @PostMapping\n    public AlarmResponse insertRule(@ServiceParam ServiceName serviceName, @RequestBody Rule rule) {\n        if (Rule.isRuleInvalidForPost(rule)) {\n            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"there is not applicationId/checkerName/userGroupId/threashold to insert alarm rule\");\n        }\n        final String ruleId = alarmService.insertRule(rule);\n        return new AlarmResponse(Result.SUCCESS, ruleId);\n    }\n\n    @PreAuthorize(\"@naverPermissionEvaluator.hasAlarmPermission(#serviceName.getName(), #rule.getApplicationName(), T(com.navercorp.pinpoint.web.security.PermissionChecker).PERMISSION_ALARM_EDIT_ALARM_ONLY_MANAGER)\")\n    @DeleteMapping\n    public Response deleteRule(@ServiceParam ServiceName serviceName, @RequestBody Rule rule) {\n        if (StringUtils.isEmpty(rule.getRuleId())) {\n            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"there is not ruleId to delete alarm rule\");\n        }\n        alarmService.deleteRule(rule);\n        return SimpleResponse.ok();\n    }\n\n    @GetMapping(params = USER_GROUP_ID_PARAMS)\n    public List<Rule> getRulesByUserGroup(@ServiceParam ServiceName serviceName, @RequestParam(value = USER_GROUP_ID_PARAMS) @NotBlank String userGroupId) {\n        return alarmService.selectRuleByUserGroupId(userGroupId);\n    }\n\n    @GetMapping(params = APPLICATION_ID_PARAMS)\n    public List<Rule> getRulesByApplication(@ServiceParam ServiceName serviceName, @RequestParam(value = APPLICATION_ID_PARAMS) @NotBlank String applicationName) {\n        return alarmService.selectRuleByApplicationName(applicationName);\n    }\n\n    @PreAuthorize(\"@naverPermissionEvaluator.hasAlarmPermission(#serviceName.getName(), #rule.getApplicationName(), T(com.navercorp.pinpoint.web.security.PermissionChecker).PERMISSION_ALARM_EDIT_ALARM_ONLY_MANAGER)\")\n    @PutMapping\n    public Response updateRule(@ServiceParam ServiceName serviceName, @RequestBody Rule rule) {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/web/src/main/java/com/navercorp/pinpoint/web/authorization/controller/AlarmController.java#L59-L95","documentation":"DELETE /alarmRule requires the rule's ID in the request body; when rule.getRuleId() is empty the controller refuses the delete with HTTP 400, because without a ruleId the service cannot identify which rule to remove.","triggerScenarios":"Sending a DELETE /alarmRule request with a Rule JSON body lacking the ruleId field or containing an empty string.","commonSituations":"Clients that built the delete body from a rule object fetched before rules were re-created (stale/empty ruleId); scripts that only pass applicationName and checkerName.","solutions":["Fetch the rule list (GET /alarmRule) and copy its ruleId into the delete body","Ensure ruleId is a non-empty string in the DELETE JSON payload","If the rule was recreated, re-fetch its ID rather than reusing a cached one"],"exampleFix":"// before\nDELETE /alarmRule {\"applicationName\":\"myApp\",\"checkerName\":\"ERROR_RATE\"}\n// after\nDELETE /alarmRule {\"ruleId\":\"1723456789_0\",\"applicationName\":\"myApp\",\"checkerName\":\"ERROR_RATE\"}","handlingStrategy":"validation","validationCode":"if (!rule.ruleId) throw new Error(\"ruleId is required to delete an alarm rule\");","typeGuard":"function hasRuleId(r) { return r != null && typeof r.ruleId === 'string' && r.ruleId.length > 0; }","tryCatchPattern":"try { ... } catch (HttpClientErrorException e) { if (e.getStatusCode().value() == 400) { /* re-fetch ruleId then retry */ } else { throw e; } }","preventionTips":["Always GET the rule list and use its ruleId for deletes","Never build delete bodies from partially populated rule objects","Re-fetch ruleId after rules are recreated"],"tags":["http-400","validation","alarm-rule","missing-identifier"],"backgroundTag":"empty-required-field","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}