apache/superset · warning · NoResultFound

Tagged object with object_id: {object_id}

Error message

Tagged object with object_id: {object_id}                     object_type: {object_type}                     and tag name: "{tag_name}" could not be found

What it means

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.

Source

Thrown at superset/daos/tag.py:89

    @staticmethod
    def delete_tagged_object(
        object_type: ObjectType, object_id: int, tag_name: str
    ) -> None:
        """
        deletes a tagged object by the object_id, object_type, and tag_name
        """
        tag = TagDAO.find_by_name(tag_name.strip())
        if not tag:
            raise NoResultFound(message=f"Tag with name {tag_name} does not exist.")

        tagged_object = db.session.query(TaggedObject).filter(
            TaggedObject.tag_id == tag.id,
            TaggedObject.object_type == object_type,
            TaggedObject.object_id == object_id,
        )
        if not tagged_object:
            raise NoResultFound(
                message=f'Tagged object with object_id: {object_id} \
                    object_type: {object_type} \
                    and tag name: "{tag_name}" could not be found'
            )

        db.session.delete(tagged_object.one())

    @staticmethod
    def delete_tags(tag_names: list[str]) -> None:
        """
        deletes tags from a list of tag names
        """
        tags_to_delete = []
        for name in tag_names:
            tag_name = name.strip()
            if not TagDAO.find_by_name(tag_name):
                raise NoResultFound(message=f"Tag with name {tag_name} does not exist.")
            tags_to_delete.append(tag_name)

View on GitHub (pinned to f4587218dd)

Solutions

  1. Check existence with .one_or_none() on the association before deleting, mirroring the fix the DAO itself needs.
  2. Make the operation idempotent: treat 'association already absent' as success.
  3. 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.
  4. Avoid racing parallel untag requests for the same object; serialize them or accept idempotent no-ops.

Example fix

# before
TagDAO.delete_tagged_object(object_type, object_id, tag_name)  # missing association -> raw sqlalchemy NoResultFound (500)

# after
from superset.models.tags import TaggedObject, ObjectType
assoc = (
    db.session.query(TaggedObject)
    .filter(
        TaggedObject.tag_id == tag.id,
        TaggedObject.object_type == object_type,
        TaggedObject.object_id == object_id,
    )
    .one_or_none()
)
if assoc:
    db.session.delete(assoc)
# else: association already gone — no-op
Defensive patterns

Strategy: validation

Validate before calling

from superset import db
from superset.models.tags import TaggedObject, ObjectType

def tagged_object_exists(tag_id: int, object_type: ObjectType, object_id: int) -> bool:
    return db.session.query(TaggedObject.id).filter(
        TaggedObject.tag_id == tag_id,
        TaggedObject.object_type == object_type,
        TaggedObject.object_id == object_id,
    ).first() is not None

Try / catch

from sqlalchemy.exc import NoResultFound  # current DAO leaks the raw SA error
try:
    TagDAO.delete_tagged_object(object_type, object_id, tag_name)
except NoResultFound:
    pass  # association already absent — idempotent success

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/superset@f4587218dd (2026-08-14). Data as JSON: /api/errors/65e84818f474ff79. Report an issue: GitHub.