{"record":{"id":"7bfcfa6653999b47","repo":"apache/shenyu","slug":"shenyu-this-discovery-has-discoveryhandler-can-t-be-delete","errorCode":null,"errorMessage":"shenyu this discovery has discoveryHandler can't be delete","messagePattern":"shenyu this discovery has discoveryHandler can't be delete","errorType":"exception","errorClass":"ShenyuException","httpStatus":null,"severity":"error","filePath":"shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryServiceImpl.java","lineNumber":196,"sourceCode":"                    .id(UUIDUtils.getInstance().generateShortUuid())\n                    .discoveryHandlerId(discoveryHandlerDO.getId())\n                    .selectorId(selectorDO.getId())\n                    .pluginName(discoveryConfigRegisterDTO.getPluginName()).build();\n            discoveryRelMapper.insertSelective(discoveryRefDO);\n            discoveryHandlerMapper.insertSelective(discoveryHandlerDO);\n        }\n        DiscoveryProcessor discoveryProcessor = discoveryProcessorHolder.chooseProcessor(discoveryConfigRegisterDTO.getDiscoveryType());\n        discoveryProcessor.createDiscovery(discoveryDO);\n        discoveryProcessor.createProxySelector(DiscoveryTransfer.INSTANCE.mapToDTO(discoveryHandlerDO), proxySelectorDTO);\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public String delete(final String discoveryId, final String namespaceId) {\n        List<DiscoveryHandlerDO> discoveryHandlerDOS = discoveryHandlerMapper.selectByDiscoveryId(discoveryId);\n        if (CollectionUtils.isNotEmpty(discoveryHandlerDOS)) {\n            LOG.warn(\"shenyu this discovery has discoveryHandler can't be delete\");\n            throw new ShenyuException(\"shenyu this discovery has discoveryHandler can't be delete\");\n        }\n        DiscoveryDO discoveryDO = discoveryMapper.selectById(discoveryId);\n        if (Objects.isNull(discoveryDO) || !Objects.equals(discoveryDO.getNamespaceId(), namespaceId)) {\n            LOG.warn(\"shenyu discovery {} is not found in namespace {}\", discoveryId, namespaceId);\n            throw new ShenyuException(\"shenyu this discovery is not found in current namespace\");\n        }\n        DiscoveryProcessor discoveryProcessor = discoveryProcessorHolder.chooseProcessor(discoveryDO.getDiscoveryType());\n        discoveryProcessor.removeDiscovery(discoveryDO);\n        discoveryMapper.delete(discoveryId, namespaceId);\n        return ShenyuResultMessage.DELETE_SUCCESS;\n    }\n\n    private DiscoveryVO create(final DiscoveryDTO discoveryDTO) {\n        if (Objects.isNull(discoveryDTO)) {\n            return null;\n        }\n        Timestamp currentTime = new Timestamp(System.currentTimeMillis());\n        DiscoveryDO discoveryDO = DiscoveryDO.builder()","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryServiceImpl.java#L178-L214","documentation":"DiscoveryServiceImpl.delete() refuses to delete a discovery config that still has discovery handler rows attached: if discoveryHandlerMapper.selectByDiscoveryId() returns rows, it throws ShenyuException \"shenyu this discovery has discoveryHandler can't be delete\". Handlers (proxy selector bindings) reference the discovery, so deleting it would leave dangling bindings; the handlers must be removed first.","triggerScenarios":"Calling DELETE /discovery/{id} (with a matching namespaceId) while the discovery still has discovery handlers bound via bindingDiscovery or dashboard-created handler records; cascading cleanup that removes discoveries before handlers.","commonSituations":"Users deleting a discovery from the dashboard while a proxy selector still points at it; scripts cleaning up config in the wrong order; stale handlers left behind after a selector was removed out-of-band.","solutions":["Delete the discovery handlers referencing this discovery first (via the dashboard or the discovery-handler delete API), then retry the discovery delete.","Identify dependent selectors/proxy bindings and remove or re-point them before deleting.","If the handlers are orphaned, clean up the discovery_handler rows in the DB, then retry."],"exampleFix":"// before\nDELETE /discovery/{discoveryId}?namespaceId=ns1  // throws: has discoveryHandler\n// after\nDELETE /discovery/handler/{handlerId}?namespaceId=ns1  // remove handlers first\nDELETE /discovery/{discoveryId}?namespaceId=ns1  // now succeeds","handlingStrategy":"try-catch","validationCode":"// before deleting, check for dependent handlers\nList<DiscoveryHandlerDO> handlers = discoveryHandlerMapper.selectByDiscoveryId(discoveryId);\nif (CollectionUtils.isNotEmpty(handlers)) {\n    handlers.forEach(h -> discoveryHandlerService.delete(h.getId(), namespaceId));\n}\ndiscoveryService.delete(discoveryId, namespaceId);","typeGuard":null,"tryCatchPattern":"try {\n    discoveryService.delete(discoveryId, namespaceId);\n} catch (ShenyuException e) {\n    if (e.getMessage().contains(\"discoveryHandler can't be delete\")) {\n        // delete bound discovery handlers first, then retry\n    }\n    throw e;\n}","preventionTips":["Delete child resources (discovery handlers / proxy selector bindings) before the parent discovery.","In cleanup scripts, iterate handlers per discoveryId before removing the discovery.","Check the dashboard for bound selectors before deleting a discovery config."],"tags":["referential-integrity","discovery","delete-conflict"],"backgroundTag":"invalid-state-transition","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}