Skip to content

Alert builder allows unreachable resource + thread-event-type combinations (silent no-op) #27889

@manerow

Description

@manerow

Summary

A Notification alert configured with resource = glossaryTerm and event-type filter = Thread Created / Thread Updated never fires. No notifications are delivered; the alert's Recent Events tab shows zero events; no error, log entry, or UI warning is surfaced.

The same pattern reproduces with other resource + event-type combinations that the backend cannot match. The alert builder UI lets users save such combinations as if they were valid.

Why the alert never matches

After the Task System Redesign (#25894), the three "thread sub-types" that historically lived inside Thread evolved into different shapes, with different change-event signatures:

Sub-type Status Change-event entityType eventType values emitted
Task First-class entity task entityCreated / entityUpdated / entityDeleted
Announcement First-class entity announcement entityCreated / entityUpdated / entityDeleted
Conversation Remains Thread.type=Conversation THREAD threadCreated / threadUpdated / postCreated / postUpdated

The backend resource gate in AlertUtil.shouldTriggerAlert (openmetadata-service/src/main/java/org/openmetadata/service/events/subscription/AlertUtil.java:137-166) handles thread events with this branch:

if (event.getEntityType().equals(THREAD)
    && (resource.equals("announcement")
        || resource.equals("task")
        || resource.equals("conversation"))) {
  Thread thread = AlertsRuleEvaluator.getThread(event);
  return resource.equalsIgnoreCase(thread.getType().value());
}
...
return config.getResources().contains(event.getEntityType());

A threadCreated event has entityType=THREAD. When the alert resource is glossaryTerm, neither this branch nor the fall-through getResources().contains(event.getEntityType()) matches. The event is dropped at the resource gate, before the user-configured filterByEventType is even evaluated. Nothing is recorded as a delivery attempt and nothing surfaces in Recent Events.

The UI is the source of the misleading experience: openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.tsx:1169-1182 populates the filterByEventType multiselect from the entire EventType enum (getSelectOptionsFromEnum(EventType)) regardless of which resource was selected at step 1. There is no per-resource restriction on the event types offered, so customers are presented with combinations that the gate cannot match.

Other unmatchable combinations from the same root cause

The same shape of mismatch occurs in additional combinations introduced by the Task redesign:

  • task + filterByEventType=[taskCreated] or [taskUpdated] — these EventType values are emitted by nothing in the current code base. The new Task entity emits entityCreated/entityUpdated like any other entity. Alerts filtering on taskCreated/taskUpdated never match.
  • task + filterByEventType=[taskResolved] or [taskClosed] — emitted only by the legacy /v1/feed/tasks/{id}/resolve|close routes, reachable today only via one governance workflow (Recognizer Feedback). New Task entity transitions do not emit them.

Task lifecycle transitions are silent

A related issue, surfaced during investigation: POST /api/v1/tasks/{id}/resolve and POST /api/v1/tasks/{id}/close produce zero change events. Verified empirically on 1.12.0-SNAPSHOT:

Operation Endpoint Change-event emitted
Create POST /api/v1/tasks entityType=task, eventType=entityCreated
Patch PATCH /api/v1/tasks/{id} entityType=task, eventType=entityUpdated
Resolve (Open → Approved/Rejected/Completed) POST /api/v1/tasks/{id}/resolve none
Close (Open → Cancelled) POST /api/v1/tasks/{id}/close none
Delete DELETE /api/v1/tasks/{id} entityType=task, eventType=entityDeleted

Mechanism: TaskResource.resolveTask (line 774) and closeTask (line 965) return Response.ok(resolvedTask).build() without setting CHANGE_CUSTOM_HEADER. The response interceptor ChangeEventHandler only emits a change event when that header is present (or when the status is CREATED), so the resolve/close turnaround yields nothing. The status mutation itself happens inside TaskWorkflowHandler via internal taskRepository.update(...) calls, which are not HTTP turnarounds and so never reach the interceptor either.

The semantically most meaningful Task transitions — resolution, closure, reopening — therefore produce no row in change_event, no audit-log entry, no notification delivery, no Recent Events tab entry, no webhook fire, no email. They are invisible to the entire downstream pipeline.

Steps to reproduce (the customer-reported case)

  1. Create a Notification alert with Resource = Glossary Term.
  2. Add filter Event Type with values Thread Created and Thread Updated.
  3. Add any destination (webhook/email/Slack).
  4. Create a glossary term and start a conversation on it. Optionally edit/resolve the thread.

Reproduction confirmation

Verified on 1.12.0-SNAPSHOT (60a2e6546e) with two side-by-side webhook subscriptions on the same conversation thread:

Subscription Recent Events Webhook fired
resource: conversation, no filters threadCreated, threadUpdated, postCreated
resource: glossaryTerm + filterByEventType: [threadCreated, threadUpdated] 0 events

Independent verification of the entity model: creating a Task via POST /api/v1/tasks produces a single row in change_event with entityType=task, eventType=entityCreated; resolving and closing the same Task produce no further rows.

Reference

Internal discussion: https://collate-inc.slack.com/archives/C096NDJQAJW/p1777900803665159
Fix design: ALERTING_AFTER_TASK_REDESIGN.md

Metadata

Metadata

Type

No fields configured for Bug.

Projects

Status

No status

Status

In Progress 🏗️

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions