Isabel
06/24/2026, 8:05 AMMarvin
06/24/2026, 8:05 AMMarvin
06/24/2026, 8:08 AMtag = X AND tag != Y
If you want “alert for tag X, except when tag Y is also present”, you’ll want a custom event trigger using match_related.
Example logic:
- include runs with tag X
- exclude runs with tag Y
Like this:
json
{
"trigger": {
"type": "event",
"expect": ["prefect.flow-run.Failed"],
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.X"
},
{
"prefect.resource.role": "tag",
"prefect.resource.id": "!prefect.tag.Y"
}
]
}
}
So for your case, if:
- X = your “real alertable” tag
- Y = your “test” tag
then this will alert only for runs tagged X and not tagged Y.
A few notes:
- match_related supports negation with !
- separate entries in match_related are effectively combined with AND
- this is the right path when the UI tag selector is too limited
Relevant docs:
- Automations
- Advanced matching for related resources
If you want, I can help you turn this into:
1. a full automation JSON/YAML, or
2. a Python SDK example for your exact tags.Isabel
06/24/2026, 8:09 AMMarvin
06/24/2026, 8:09 AMMarvin
06/24/2026, 8:11 AMmatch_related negation does not behave like “NOT EXISTS”.
If you use something like:
json
{
"match_related": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.X"
},
{
"prefect.resource.role": "tag",
"prefect.resource.id": "!prefect.tag.Y"
}
]
}
that second clause is evaluated against the list of related tag resources by checking whether any related resource matches it. That means:
- flow run tags = ["X"] -> likely works
- flow run tags = ["X", "Y"] -> can still match unexpectedly
- flow run tags = [] -> won’t match the negation clause at all
The key problem is that !prefect.tag.Y is not treated as “there is no Y tag present”; it’s treated more like “there exists a related tag whose id is not Y”.
So yes: what you hit looks like a real product limitation / semantics issue, even if it’s not currently tracked as a named known bug.
Practical implication for your case
If a run has both:
- your normal tag X
- your test tag Y
then the negation clause may still succeed because the X tag itself satisfies != Y.
So this pattern is not reliable for “include X but exclude Y”.
Docs:
- Advanced matching for related resources
- Automations
Recommended workarounds
1. Use a positive-only alert tag
Add a tag only to runs you do want alerts for, like alerting-enabled, and filter only on that.
2. Use separate naming conventions
Exclude test runs by flow/deployment naming if that is stable.
3. Route test flows to a different deployment/workspace/pool
Then alert only on the production surface.
If you want, I can help you design the best reliable workaround for your setup.
Also, since this appears to be a genuine gap and I didn’t find an existing issue/discussion, I can summarize it cleanly for a GitHub discussion if that would be useful.Isabel
06/24/2026, 8:20 AMMarvin
06/24/2026, 8:20 AMMarvin
06/24/2026, 8:20 AMtag = X AND tag = alerting_on
use match_related with two positive tag matches:
json
{
"trigger": {
"type": "event",
"expect": ["prefect.flow-run.Failed"],
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.X"
},
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.alerting_on"
}
]
}
}
That means:
- the event must be for a flow run
- the flow run must have tag X
- the flow run must also have tag alerting_on
This is much safer than trying to do X AND NOT test.
A few notes:
- replace X with your real tag name
- tag ids are formatted as prefect.tag.<tag-name>
- multiple entries in match_related are effectively combined with AND
If you’re creating this in the UI, you may need to use the advanced/custom trigger configuration rather than the simple tag picker.
If you want a concrete example, here’s a fuller skeleton:
json
{
"name": "Alert on failed X runs with alerting enabled",
"trigger": {
"type": "event",
"expect": ["prefect.flow-run.Failed"],
"posture": "Reactive",
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.X"
},
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.alerting_on"
}
]
},
"actions": [
{
"type": "send-notification"
}
]
}
Docs:
- Advanced matching for related resources
- Automations
If you want, I can help convert this into:
- a UI walkthrough,
- a Terraform spec,
- or a Python SDK automation example.Isabel
06/24/2026, 8:39 AM{
"type": "event",
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": {
"prefect.resource.id": [
"prefect.tag.X",
"prefect.tag.alerting_on"
],
"prefect.resource.role": "tag"
},
"after": [],
"expect": [
"prefect.flow-run.TimedOut",
"prefect.flow-run.Crashed"
],
"for_each": [
"prefect.resource.id"
],
"posture": "Reactive",
"threshold": 1,
"within": 0
}
but this will alert if one of the tags is present, so the "AND" does not workMarvin
06/24/2026, 8:39 AMMarvin
06/24/2026, 8:39 AMjson
"match_related": {
"prefect.resource.id": [
"prefect.tag.X",
"prefect.tag.alerting_on"
],
"prefect.resource.role": "tag"
}
That array is treated as OR, not AND.
So this means:
- match a related resource with role tag
- whose id is prefect.tag.X or prefect.tag.alerting_on
That’s why either tag alone triggers the automation.
To require both tags, you need match_related to be a list of separate objects:
json
{
"type": "event",
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.X"
},
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.alerting_on"
}
],
"after": [],
"expect": [
"prefect.flow-run.TimedOut",
"prefect.flow-run.Crashed"
],
"for_each": [
"prefect.resource.id"
],
"posture": "Reactive",
"threshold": 1,
"within": 0
}
That structure is the important part:
- one object with an array of ids -> OR
- array of objects -> AND across those conditions
So your earlier attempt was very close — it was just using the OR form.
Relevant docs:
- Advanced matching for related resources
If you want, I can also help check whether the UI is rewriting your JSON when you save it, because that can sometimes be the next gotcha.Isabel
06/24/2026, 8:44 AM{
"type": "event",
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.X"
},
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.alerting_on"
}
],
"after": [],
"expect": [
"prefect.flow-run.TimedOut",
"prefect.flow-run.Crashed"
],
"for_each": [
"prefect.resource.id"
],
"posture": "Reactive",
"threshold": 1,
"within": 0
}
WorksMarvin
06/24/2026, 8:44 AMMarvin
06/24/2026, 8:45 AMmatch_related object with a list of values for prefect.resource.id behaves as OR
- a list of separate match_related objects behaves as AND
So if the UI visually suggests “tag X and alerting_on” while producing the single-object/list-of-values form, that’s definitely confusing.
Glad to hear this version works:
json
{
"type": "event",
"match": {
"prefect.resource.id": "prefect.flow-run.*"
},
"match_related": [
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.X"
},
{
"prefect.resource.role": "tag",
"prefect.resource.id": "prefect.tag.alerting_on"
}
],
"after": [],
"expect": [
"prefect.flow-run.TimedOut",
"prefect.flow-run.Crashed"
],
"for_each": [
"prefect.resource.id"
],
"posture": "Reactive",
"threshold": 1,
"within": 0
}
If you’d like, I can turn your finding into a concise GitHub discussion draft, since:
- it’s a real UX gotcha
- it could help other users
- I didn’t find an existing discussion covering this exact confusionBring your towel and join one of the fastest growing data communities. Welcome to our second-generation open source orchestration platform, a completely rethought approach to dataflow automation.
Powered by