{"record":{"id":"e98edb82973febfd","repo":"alibaba/spring-ai-alibaba","slug":"failed-to-delete-experiment","errorCode":null,"errorMessage":"Failed to delete experiment","messagePattern":"Failed to delete experiment","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-start/src/main/java/com/alibaba/cloud/ai/studio/admin/service/impl/ExperimentServiceImpl.java","lineNumber":321,"sourceCode":"        if (id == null) {\n            throw new IllegalArgumentException(\"Experiment ID cannot be null\");\n        }\n        \n        // 检查实验是否存在\n        ExperimentDO experimentDO = experimentMapper.selectById(id);\n        if (experimentDO == null) {\n            throw new IllegalArgumentException(\"Experiment not found: \" + id);\n        }\n        \n        // 检查实验状态，运行中的实验不能删除\n        if (Objects.equals(experimentDO.getStatus(), ExperimentStatus.RUNNING.getCode())) {\n            throw new IllegalStateException(\"Cannot delete running experiment: \" + id);\n        }\n        \n        // 删除实验\n        int result = experimentMapper.deleteById(id);\n        if (result <= 0) {\n            throw new RuntimeException(\"Failed to delete experiment\");\n        }\n//\n//        // 删除相关的实验结果\n//        experimentResultMapper.deleteByExperimentId(id);\n        \n        log.info(\"实验删除成功: {}\", id);\n    }\n\n    @Override\n    public void restartById(Long id) {\n        //清理历史数据\n        experimentResultMapper.deleteByExperimentId(id);\n        //实验执行\n\n        ExperimentDO experimentDO = experimentMapper.selectById(id);\n\n        startExperimentExecution(experimentDO);\n","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-start/src/main/java/com/alibaba/cloud/ai/studio/admin/service/impl/ExperimentServiceImpl.java#L303-L339","documentation":"Thrown by ExperimentServiceImpl.deleteById when experimentMapper.deleteById returns 0 rows affected after the existence and status checks passed. The library interprets a non-positive delete result as a failure to remove the row.","triggerScenarios":"Concurrent deletion by another request between the selectById check and the delete; database write failure, lock timeout, or permission problem causing the DELETE to affect no rows.","commonSituations":"Two clients deleting the same experiment simultaneously; database connectivity/transaction issues in the admin server; soft-delete plugins intercepting the delete.","solutions":["Handle it idempotently: if the record is already gone, treat the delete as successful instead of throwing","Check database logs for lock/permission errors on the experiment table","Retry the operation and re-check existence first"],"exampleFix":"// before\nint result = experimentMapper.deleteById(id);\nif (result <= 0) {\n    throw new RuntimeException(\"Failed to delete experiment\");\n}\n// after\nint result = experimentMapper.deleteById(id);\nif (result <= 0 && experimentMapper.selectById(id) != null) {\n    throw new IllegalStateException(\"Failed to delete experiment: \" + id);\n}","handlingStrategy":"retry","validationCode":"if (experimentMapper.selectById(id) == null) return; // nothing to delete, already gone","typeGuard":"null","tryCatchPattern":"try { service.deleteById(id); } catch (RuntimeException e) { if (experimentMapper.selectById(id) == null) { /* treat as success */ } else { throw e; } }","preventionTips":["Treat delete of a missing row as idempotent success","Investigate DB lock/permission errors in logs","Avoid concurrent deletes of the same experiment"],"tags":["database","mybatis","concurrency"],"backgroundTag":"database-write-failed","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}