{"record":{"id":"65e84818f474ff79","repo":"apache/superset","slug":"tagged-object-with-object-id-object-id","errorCode":null,"errorMessage":"Tagged object with object_id: {object_id}                     object_type: {object_type}                     and tag name: \"{tag_name}\" could not be found","messagePattern":"Tagged object with object_id: (.+?)                     object_type: (.+?)                     and tag name: \"(.+?)\" could not be found","errorType":"exception","errorClass":"NoResultFound","httpStatus":null,"severity":"warning","filePath":"superset/daos/tag.py","lineNumber":89,"sourceCode":"\n    @staticmethod\n    def delete_tagged_object(\n        object_type: ObjectType, object_id: int, tag_name: str\n    ) -> None:\n        \"\"\"\n        deletes a tagged object by the object_id, object_type, and tag_name\n        \"\"\"\n        tag = TagDAO.find_by_name(tag_name.strip())\n        if not tag:\n            raise NoResultFound(message=f\"Tag with name {tag_name} does not exist.\")\n\n        tagged_object = db.session.query(TaggedObject).filter(\n            TaggedObject.tag_id == tag.id,\n            TaggedObject.object_type == object_type,\n            TaggedObject.object_id == object_id,\n        )\n        if not tagged_object:\n            raise NoResultFound(\n                message=f'Tagged object with object_id: {object_id} \\\n                    object_type: {object_type} \\\n                    and tag name: \"{tag_name}\" could not be found'\n            )\n\n        db.session.delete(tagged_object.one())\n\n    @staticmethod\n    def delete_tags(tag_names: list[str]) -> None:\n        \"\"\"\n        deletes tags from a list of tag names\n        \"\"\"\n        tags_to_delete = []\n        for name in tag_names:\n            tag_name = name.strip()\n            if not TagDAO.find_by_name(tag_name):\n                raise NoResultFound(message=f\"Tag with name {tag_name} does not exist.\")\n            tags_to_delete.append(tag_name)","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/daos/tag.py#L71-L107","documentation":"Raised by TagDAO.delete_tagged_object for a missing TaggedObject join row. The code's intent is to complain when no association between (tag, object_type, object_id) exists, but the guard is defective: `db.session.query(...).filter(...)` builds a Query object which is always truthy, so the branch is unreachable and the subsequent tagged_object.one() raises sqlalchemy.exc.NoResultFound instead. The literal in the source also contains embedded line-continuation backslashes, so the rendered message has unexpected whitespace.","triggerScenarios":"Deleting a tag-object association that was already removed; passing an object_id/object_type pair that was never tagged with this tag; concurrent deletes of the same association.","commonSituations":"Double-submit of an untag action in the UI; scripts that untag objects by id after the tag was reassigned; any code path that reaches the broken guard — it silently converts to an unwrapped SQLAlchemy NoResultFound (HTTP 500) rather than the intended typed error.","solutions":["Check existence with .one_or_none() on the association before deleting, mirroring the fix the DAO itself needs.","Make the operation idempotent: treat 'association already absent' as success.","Upstream-quality note: the DAO guard should use `tagged_object.one_or_none()` and raise on None; report or patch it, since the current code leaks sqlalchemy.exc.NoResultFound.","Avoid racing parallel untag requests for the same object; serialize them or accept idempotent no-ops."],"exampleFix":"# before\nTagDAO.delete_tagged_object(object_type, object_id, tag_name)  # missing association -> raw sqlalchemy NoResultFound (500)\n\n# after\nfrom superset.models.tags import TaggedObject, ObjectType\nassoc = (\n    db.session.query(TaggedObject)\n    .filter(\n        TaggedObject.tag_id == tag.id,\n        TaggedObject.object_type == object_type,\n        TaggedObject.object_id == object_id,\n    )\n    .one_or_none()\n)\nif assoc:\n    db.session.delete(assoc)\n# else: association already gone — no-op","handlingStrategy":"validation","validationCode":"from superset import db\nfrom superset.models.tags import TaggedObject, ObjectType\n\ndef tagged_object_exists(tag_id: int, object_type: ObjectType, object_id: int) -> bool:\n    return db.session.query(TaggedObject.id).filter(\n        TaggedObject.tag_id == tag_id,\n        TaggedObject.object_type == object_type,\n        TaggedObject.object_id == object_id,\n    ).first() is not None","typeGuard":null,"tryCatchPattern":"from sqlalchemy.exc import NoResultFound  # current DAO leaks the raw SA error\ntry:\n    TagDAO.delete_tagged_object(object_type, object_id, tag_name)\nexcept NoResultFound:\n    pass  # association already absent — idempotent success","preventionTips":["Guard with one_or_none() existence checks instead of relying on the DAO's broken truthiness guard.","Make untag operations idempotent; treat already-removed associations as success.","Watch upstream for a fix to the DAO guard (it tests a Query object, which is always truthy)."],"tags":["tags","dao","not-found","bug","idempotency"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}