Pipeline isn't triggering from real traffic
Symptom: you POST to /webhook expecting a pipeline to run, and either
nothing happens, or the wrong pipeline runs, or you get an error back. In
every case, the webhook response itself tells you exactly which of the
following it is — read the "status" field before guessing.
Read the response first
Section titled “Read the response first”{"status": "accepted", "run_id": "..."}This is the only shape that means a run actually started. Anything else means one of the checks below stopped it before a run was created:
status in the response |
What it means |
|---|---|
"accepted" |
A run started. If you still don’t see it, that’s a different problem — see Webhook says accepted but the run 404s. |
"skipped_testing" |
The pipeline is stage: testing and this call didn’t pass allow_testing=true. See below. |
"deduplicated" |
A run for this same alert is already running or ran recently — not a failure, see Idempotency & deduplication. |
HTTP 400, "Unknown source" |
The ?source= (or X-Pipeline-Source header) value doesn’t match a registered parser. Registered sources are named in the error itself. |
HTTP 422, "No pipeline matched" |
The request parsed fine, but no pipeline’s trigger.match accepted it. See below. |
stage: testing is the most common cause
Section titled “stage: testing is the most common cause”Every pipeline in this series — and every pipeline by default — starts as
stage: testing. A stage: testing pipeline does not fire from real
ingestion traffic, on purpose, so a half-finished pipeline can’t
accidentally page someone:
POST /webhook?source=alertmanager# → {"status": "skipped_testing", ...} — testing pipeline, no override
POST /webhook?source=alertmanager&allow_testing=true# → {"status": "accepted", "run_id": "..."} — deliberately opted inIf you’re developing a pipeline, add &allow_testing=true to every trigger
call while you’re working on it (every tutorial in this series does
exactly this). If you’re integrating a real system — Alertmanager,
whatever’s actually calling this URL — and it’s not passing that query
param, the fix isn’t to add it there; it’s to promote the pipeline to
stage: production once it’s
actually ready, since real traffic shouldn’t need a special flag to work at
all. See Testing vs production stages for the
full reasoning.
The Run now button in the UI and a manual re-run always bypass this — they’re deliberate manual actions, not real ingestion traffic — so a pipeline that works from Run now but not from a real webhook call is almost always this, not a broken pipeline.
trigger.match conditions are AND, not OR — and case-sensitive
Section titled “trigger.match conditions are AND, not OR — and case-sensitive”trigger: match: severity: critical environment: prodEvery condition in trigger.match has to pass for this pipeline to be
selected — this is an AND, not “match any of these.” A payload with
severity: critical but environment: staging matches neither condition
partially; it simply doesn’t match this pipeline, and the resolver moves on
to the next one. If nothing matches, the whole request 422s with
"No pipeline matched for source=... severity=... labels=..." — that
detail message names exactly what VectorStep saw, which is the fastest way
to spot a typo or a case mismatch (Critical vs critical never match — a
plain scalar match is exact-equality, not case-insensitive).
If you need “any of several values,” a plain scalar won’t do it — reach for
the in operator instead:
trigger: match: environment: in: [prod, staging] # matches either, not just oneSee Webhooks — Pipeline resolution
for the full operator table (eq/ne/in/not_in/regex/gt/gte/lt/lte).
Configs are matched in load order, first match wins. If two pipelines
could both match the same payload, whichever one is more specific should be
ordered first — an accidental catch-all pipeline earlier in
pipeline_config_dir will silently claim traffic meant for a more specific
one later in the list, with no error at all, since as far as the resolver
is concerned the request was handled successfully.
Confirm the field names, not just the values
Section titled “Confirm the field names, not just the values”trigger.match keys are checked against NormalisedContext’s top-level
fields (source, severity, summary) first, then against
context.labels — so environment: prod only works if environment is
actually a label on the normalised payload, not a field the reader assumes
exists. For the Alertmanager source specifically, this means an
Alertmanager label, not something invented in the pipeline’s own YAML.
Webhooks — Normalisation layer
shows exactly what a payload becomes before matching happens — worth
checking directly if a match you’re confident should work still isn’t
firing.
Where next
Section titled “Where next”- Webhook sources — the full intake, normalisation, and resolution reference.
- Testing vs production stages — everything
else
stage: testingmutes besides trigger gating. - Webhook says accepted but the run 404s — the next thing to check once you’re confident a run genuinely started.