{"record":{"id":"737ebd7ca94dce90","repo":"apache/superset","slug":"annotation-layer-has-associated-annotations","errorCode":null,"errorMessage":"Annotation layer has associated annotations.","messagePattern":"Annotation layer has associated annotations\\.","errorType":"exception","errorClass":"AnnotationLayerDeleteIntegrityError","httpStatus":422,"severity":"error","filePath":"superset/commands/annotation_layer/delete.py","lineNumber":51,"sourceCode":"\nclass DeleteAnnotationLayerCommand(BaseCommand):\n    def __init__(self, model_ids: list[int]):\n        self._model_ids = model_ids\n        self._models: Optional[list[AnnotationLayer]] = None\n\n    @transaction(on_error=partial(on_error, reraise=AnnotationLayerDeleteFailedError))\n    def run(self) -> None:\n        self.validate()\n        assert self._models\n        AnnotationLayerDAO.delete(self._models)\n\n    def validate(self) -> None:\n        # Validate/populate model exists\n        self._models = AnnotationLayerDAO.find_by_ids(self._model_ids)\n        if not self._models or len(self._models) != len(self._model_ids):\n            raise AnnotationLayerNotFoundError()\n        if AnnotationLayerDAO.has_annotations(self._model_ids):\n            raise AnnotationLayerDeleteIntegrityError()\n","sourceCodeStart":33,"sourceCodeEnd":52,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/annotation_layer/delete.py#L33-L52","documentation":"AnnotationLayerDeleteIntegrityError raised by DeleteAnnotationLayerCommand.validate() (delete.py:51) when AnnotationLayerDAO.has_annotations(model_ids) is true — you cannot delete an annotation layer that still has annotations attached. It is a referential-integrity guard, not a failure; HTTP 404-class CommandException but semantically a 409-style conflict.","triggerScenarios":"DELETE /api/v1/annotation_layer/<id> while annotations reference the layer (annotation.layer_id FK); bulk delete where any one layer in the list still has annotations.","commonSituations":"Trying to clean up layers without first removing or migrating their annotations; import scripts creating layers+annotations then attempting layer cleanup.","solutions":["List the layer's annotations (GET /api/v1/annotation/?q=(layer:<id>)) and DELETE them first.","Alternatively move annotations to another layer via PATCH before deleting the layer.","Only after the layer is empty, re-issue DELETE /api/v1/annotation_layer/<id>."],"exampleFix":"# before\nDeleteAnnotationLayerCommand([5]).run()  # layer 5 still has annotations\n\n# after\nann_ids = [a.id for a in AnnotationDAO.find_by_layer_id(5)]\nif ann_ids:\n    DeleteAnnotationCommand(ann_ids).run()\nDeleteAnnotationLayerCommand([5]).run()","handlingStrategy":"validation","validationCode":"from superset.daos.annotation_layer import AnnotationLayerDAO\n\nif AnnotationLayerDAO.has_annotations(layer_ids):\n    blocking = {lid: fetch_annotation_count(lid) for lid in layer_ids}\n    return {\"error\": \"delete or move annotations first\", \"layers\": blocking}, 409","typeGuard":null,"tryCatchPattern":"try:\n    DeleteAnnotationLayerCommand(ids).run()\nexcept AnnotationLayerDeleteIntegrityError:\n    delete_or_move_annotations(ids); DeleteAnnotationLayerCommand(ids).run()","preventionTips":["Check has_annotations() before offering the delete-layer action in the UI.","Show the layer's annotation count in the UI so users know what blocks deletion.","Automate cleanup: delete child annotations (or PATCH them to a new layer) before deleting layers."],"tags":["annotation-layer","referential-integrity","delete","conflict"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}