apache/superset · error · DAODeleteFailedError

Failed to remove subscription for task {task_id}, user {user

Error message

Failed to remove subscription for task {task_id}, user {user_id}

What it means

Raised by TaskDAO when removing a subscriber from an alert/report task fails. The DAO performs the delete + flush inside a try block; any unexpected exception (DB error, stale task row, session issue) is re-wrapped as DAODeleteFailedError with the message naming the task_id and user_id. Only the two expected not-found cases raise it directly; everything else lands in the generic except.

Source

Thrown at superset/daos/tasks.py:331

        if not subscription:
            return None

        try:
            db.session.delete(subscription)
            db.session.flush()
            logger.info("Removed subscriber %s from task %s", user_id, task_id)

            # Return the updated task
            task = cls.find_by_id(task_id, skip_base_filter=True)
            if task:
                db.session.refresh(task)  # Ensure subscribers list is fresh
            return task

        except DAODeleteFailedError:
            raise
        except Exception as ex:
            raise DAODeleteFailedError(
                f"Failed to remove subscription for task {task_id}, user {user_id}"
            ) from ex

    @classmethod
    def set_properties_and_payload(
        cls,
        task_uuid: UUID,
        properties: TaskProperties | None = None,
        payload: dict[str, Any] | None = None,
    ) -> bool:
        """
        Perform a zero-read SQL UPDATE on properties and/or payload columns.

        This method directly writes the provided values without reading first.
        The caller (TaskContext) is responsible for maintaining the authoritative
        cached state and passing complete values to write.

        This method is designed for internal task updates (progress, is_abortable)

View on GitHub (pinned to f4587218dd)

Solutions

  1. Check the chained cause (ex.__cause__) in logs — the original exception says whether it is a connectivity issue, integrity error, or missing row.
  2. Verify the task_id still exists (TaskDAO.find_by_id) and the user is currently in its subscribers list before retrying the removal.
  3. If it is a connection issue, fix the metadata DB session/pool config (SQLALCHEMY_DATABASE_URI pool_pre_ping, pool_recycle) and retry the API call.
  4. For race conditions (row already gone), treat the 4xx response as success — the end state (user not subscribed) is already achieved.

Example fix

// before
task = TaskDAO.remove_subscriber(task_id, user_id)

// after
task = TaskDAO.remove_subscriber(task_id, user_id)  # may raise DAODeleteFailedError
# caller:
try:
    TaskDAO.remove_subscriber(task_id, user_id)
except DAODeleteFailedError:
    current = TaskDAO.find_by_id(task_id, skip_base_filter=True)
    if current and user_id in [u.id for u in current.owners]:
        raise  # real failure
    # else: already unsubscribed, treat as done
Defensive patterns

Strategy: try-catch

Validate before calling

from superset.daos.task import TaskDAO
task = TaskDAO.find_by_id(task_id, skip_base_filter=True)
if task and any(u.id == user_id for u in task.owners):
    TaskDAO.remove_subscriber(task_id, user_id)

Try / catch

from superset.daos.exceptions import DAODeleteFailedError
try:
    TaskDAO.remove_subscriber(task_id, user_id)
except DAODeleteFailedError as ex:
    logger.warning("unsubscribe failed: %s", ex, exc_info=ex.__cause__)
    # idempotent recovery: verify end state
    task = TaskDAO.find_by_id(task_id, skip_base_filter=True)
    if task and any(u.id == user_id for u in task.owners):
        raise

Prevention

When it happens

Trigger: DELETE call to remove a user's subscription from a report/alert (TaskDAO.remove_subscriber / the REST endpoint that calls it) where db.session.flush() or the preceding delete raises: task UUID deleted concurrently, metadata DB connection dropped mid-transaction, or a subscriber row already removed by a racing request.

Common situations: Two browser tabs unfollowing the same report simultaneously; metadata database (SQLAlchemy session) in a bad state after a long idle connection; operating on a task that was just deleted by an admin.

Related errors


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