Skip to content

Alerts for skipped tests/models stay pending in alerts_v2 forever (not sent, not marked skipped) #2385

Description

@masukai

Describe the bug

When a dbt test is skipped (for example because its upstream model failed in dbt build), edr monitor inserts an alert for it into alerts_v2, but never processes it afterwards. The default status filter of edr monitor drops it before sending, and dropped alerts are not marked skipped either. So the row stays status = 'pending' forever.

In a long-running project these rows pile up indefinitely. Anyone who queries alerts_v2 sees a large number of "pending" alerts and can reasonably read it as "alerting is broken", although nothing will ever be sent for them.

As far as I can tell from the code (I may be missing something):

#2363 fixed the same pattern for the reused status. skipped seems to be the remaining case.

To Reproduce

A self-contained project on DuckDB. No real Slack is needed: a local stand-in webhook answers 200 ok.

  1. Install: pip install "elementary-data[duckdb]==0.26.0" "dbt-core==1.10.*" "dbt-duckdb==1.10.*"
  2. Create the project:

dbt_project.yml

name: repro
version: "1.0"
profile: repro
models:
  elementary:
    +schema: elementary

packages.yml

packages:
  - package: elementary-data/elementary
    version: 0.26.0

models/upstream_broken.sql (fails at run time, so the test on it is skipped)

select * from table_that_does_not_exist

models/healthy.sql

select 1 as id

models/schema.yml (accepted_values on healthy is a control that fails normally)

version: 2
models:
  - name: upstream_broken
    columns:
      - name: id
        data_tests:
          - not_null
  - name: healthy
    columns:
      - name: id
        data_tests:
          - accepted_values:
              values: [2]

profiles.yml (the path must be absolute, because edr runs dbt from its own bundled project directory)

repro:
  target: dev
  outputs:
    dev: {type: duckdb, path: "{{ env_var('REPRO_DB') }}", schema: main}
elementary:
  target: dev
  outputs:
    dev: {type: duckdb, path: "{{ env_var('REPRO_DB') }}", schema: main_elementary}

fake_slack_webhook.py

from http.server import BaseHTTPRequestHandler, HTTPServer
class H(BaseHTTPRequestHandler):
    def do_POST(self):
        self.rfile.read(int(self.headers.get("Content-Length", 0)))
        self.send_response(200); self.end_headers(); self.wfile.write(b"ok")
HTTPServer(("127.0.0.1", 8765), H).serve_forever()
  1. Run:
export DBT_PROFILES_DIR=. REPRO_DB="$PWD/repro.duckdb"
dbt deps
dbt run -s elementary
dbt build            # upstream_broken: ERROR, not_null_upstream_broken_id: SKIP, accepted_values_healthy_id__2: FAIL
python fake_slack_webhook.py &
edr monitor --profiles-dir . --project-dir . --slack-webhook http://127.0.0.1:8765/hook
  1. Check alerts_v2:
select type,
       json_extract_string(data, '$.test_name')       as test_name,
       json_extract_string(data, '$.model_unique_id') as model,
       json_extract_string(data, '$.status')          as result_status,
       status                                          as alert_status
from main_elementary.alerts_v2;

Result:

type test_name model result_status alert_status
model model.repro.upstream_broken error sent
test accepted_values_healthy_id__2 model.repro.healthy fail sent
test not_null_upstream_broken_id model.repro.upstream_broken skipped pending

The monitor log says Update skipped alerts (0) and Update sent alerts (2).

Running dbt build and edr monitor a second time (also with --dbt-vars '{"disable_skipped_test_alerts": true, "disable_skipped_model_alerts": true}') adds one more skipped / pending row, and the first one is still pending. Once detected_at falls out of --days-back, nothing will ever read these rows again.

Expected behavior

An alert that edr monitor has decided not to send should not stay pending. Either of these would work for us:

  1. Do not insert alerts for skipped results when disable_skipped_test_alerts / disable_skipped_model_alerts are on, the same way fix: treat reused status as success/pass in alert population #2363 handles reused, or
  2. Mark alerts removed by the status filter as skipped in alerts_v2, so pending only means "not processed yet".

Option 1 seems closer to the existing vars. But the status filter is configurable (--filters statuses:...), so option 2 would also cover other statuses that a filter excludes.

Screenshots

N/A

Environment (please complete the following information):

  • Elementary CLI (edr) version: 0.26.0
  • Elementary dbt package version: 0.26.0
  • dbt version you're using: dbt-core 1.10.23 (reproduction), also seen with dbt-core 1.12.5 on BigQuery
  • Data warehouse: DuckDB with dbt-duckdb 1.10.1 (reproduction), BigQuery with dbt-bigquery 1.12.1 (where we first noticed it)
  • Infrastructure details: reproduction on macOS, local. In production, edr monitor runs daily on a container job with --days-back 7

Additional context

As a workaround, we run a daily job that sets status = 'skipped' on these rows, using the same UPDATE that update_skipped_alerts issues.

Would you be willing to contribute a fix for this issue?

Yes, I'd be happy to open a PR if you can confirm which direction you prefer.

Activity

  1. masukai commented on Oct 20, 2026

    @masukai
    Author

    A correction to the "Expected behavior" section above, from checking it against the docs and the code while working on #2399:

    • Option 2 (mark alerts removed by the status filter as skipped) is not safe: the docs describe running edr monitor several times with different filters, so an alert dropped by one run's filter must stay pending for another run.
    • Option 1 as I first described it (don't insert skipped rows when disable_skipped_*_alerts is on) breaks edr monitor --filters statuses:skipped, which the docs give as the way to receive skipped alerts, because the rows are never inserted. Details in fix: don't insert skipped test/model results into alerts_v2 when disable_skipped_*_alerts is on #2399, which is now a draft.

    An approach that may fit better is to let the current run's status filter decide which statuses are inserted when populating alerts_v2. I'd like to hear which direction you prefer before changing anything else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions