apache/superset · error · AnnotationNotFoundError

Annotation not found.

Error message

Annotation not found.

What it means

AnnotationNotFoundError from UpdateAnnotationCommand.validate() (update.py:61): AnnotationDAO.find_by_id(self._model_id) returned None, so the annotation being PATCHed no longer exists. HTTP 404. The command is instantiated with a model id from the URL, so this means the target itself is gone, not a related object.

Source

Thrown at superset/commands/annotation_layer/annotation/update.py:61

        self._model_id = model_id
        self._properties = data.copy()
        self._model: Optional[Annotation] = None

    @transaction(on_error=partial(on_error, reraise=AnnotationUpdateFailedError))
    def run(self) -> Model:
        self.validate()
        assert self._model
        return AnnotationDAO.update(self._model, self._properties)

    def validate(self) -> None:
        exceptions: list[ValidationError] = []
        layer_id: Optional[int] = self._properties.get("layer")
        short_descr: str = self._properties.get("short_descr", "")

        # Validate/populate model exists
        self._model = AnnotationDAO.find_by_id(self._model_id)
        if not self._model:
            raise AnnotationNotFoundError()
        # Validate/populate layer exists
        if layer_id:
            annotation_layer = AnnotationLayerDAO.find_by_id(layer_id)
            if not annotation_layer:
                raise AnnotationLayerNotFoundError()
            self._properties["layer"] = annotation_layer

            # Validate short descr uniqueness on this layer
            if not AnnotationDAO.validate_update_uniqueness(
                layer_id,
                short_descr,
                annotation_id=self._model_id,
            ):
                exceptions.append(AnnotationUniquenessValidationError())
        else:
            self._properties["layer"] = self._model.layer

        # validate date time sanity

View on GitHub (pinned to f4587218dd)

Solutions

  1. GET /api/v1/annotation/<id> first; if 404, remove the row from the client list instead of patching.
  2. Refresh the annotation list after concurrent deletes and reapply the edit to a live row.
  3. In scripts, guard updates with an existence check and treat 404 as a no-op.

Example fix

# before
UpdateAnnotationCommand(model_id=42, properties).run()

# after
from superset.daos.annotation_layer import AnnotationDAO
if AnnotationDAO.find_by_id(42) is None:
    log.info("annotation 42 gone; skipping update")
else:
    UpdateAnnotationCommand(model_id=42, properties).run()
Defensive patterns

Strategy: validation

Validate before calling

from superset.daos.annotation_layer import AnnotationDAO

if AnnotationDAO.find_by_id(annotation_id) is None:
    remove_from_client_list(annotation_id); return  # skip update

Try / catch

try:
    UpdateAnnotationCommand(model_id, properties).run()
except AnnotationNotFoundError:
    refresh_annotation_list(); notify_user("annotation was deleted by another session")

Prevention

When it happens

Trigger: PATCH /api/v1/annotation/<id> after that annotation was deleted in another tab/session; editing from a stale list view; replaying an update request against a different metadata DB.

Common situations: Two users editing the same annotation layer simultaneously; long-open editor pages whose row was removed; environment mismatch in scripted updates.

Related errors


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