Why didn't my watch fire?
Something happened in the world, and Watcher said nothing. Work through this in order — it takes about two minutes.
Step 1 — find the run outcome
Section titled “Step 1 — find the run outcome”-
Open the watch’s run history: in the panel, in the app, with
pnpm watches history <id>, orGET /watches/:id/runs. -
Find the run that covers the time the event happened.
-
Read its outcome word. That single word tells you which stage stopped the run.
Step 2 — look up the outcome
Section titled “Step 2 — look up the outcome”| Outcome | What happened | What to do |
|---|---|---|
disabled | The watch is switched off, so nothing was collected or evaluated. | Enable the watch if you expected it to run. |
no-candidates | Sources ran without error but returned no items at all. | Check the source still returns content — an empty feed, an empty search, or a page that extracted nothing. |
collection-failed | Every source that was tried threw an error. | Look at the recorded source errors. A health alert is also sent for this outcome. |
nothing-new | Items were collected, but every one had already been seen on a previous run. | Nothing. This is the normal outcome on a quiet day and costs no model calls. |
not-triggered | Fresh content was evaluated and the model decided the condition was not met. | If you disagree, the condition or the sources are usually the problem, not the threshold. |
below-threshold | The model triggered, but the result did not clear the bar — either low confidence, or a quality check failed and there was no weak tier to fall back to. | Check the run record: an ungrounded quote or an ungraded source demotes an otherwise confident verdict. |
already-notified | The triggered event has the same identity as one already alerted on. | Nothing. Deduplication working as intended. |
semantic-duplicate | The event is a reworded version of one recently alerted on (embedding similarity at or above 0.92). | Nothing, unless you believe it was genuinely a different event. |
notified | An alert fired and was delivered on at least one channel. | Read the alert. Optionally label it to feed the accuracy dashboard. |
notify-failed | The alert was real and passed every check, but every configured channel failed to deliver it. | Fix the channel. Delivery is retried automatically for up to five attempts. |
Step 3 — fix by outcome
Section titled “Step 3 — fix by outcome”nothing-new
Section titled “nothing-new”Watcher fetched your sources and every item was one it had already read.
- The page changed but the extracted text did not. Common on portals where the news is in a JavaScript-rendered widget while the extracted text is a static shell. Confirm with Sources return nothing.
- The news is not on the page you are watching. Add a
searchsource so Watcher can find it wherever it landed.
not-triggered
Section titled “not-triggered”The model read genuinely new content and decided your condition was not met.
- Read the run’s reasoning. It is stored on the run record and shown in the panel — it usually says exactly why.
- Make the condition concrete. “result” is a keyword; “the official result or scorecard for X has been declared” is a question a model can answer.
- Check the content actually contains the news. If the collected text is a navigation shell, no condition will match.
- Remember: the model is instructed not to trigger on speculation or “coming soon”, and not to re-fire on a restatement of something you were already told.
below-threshold
Section titled “below-threshold”Something triggered but did not clear the bar. This is the outcome with the most possible causes — and the run record narrows it down:
| Look for | Cause | Fix |
|---|---|---|
Confidence just under your threshold |
The bar is set high | Lower threshold, or add weakThreshold so borderline hits surface as unconfirmed |
| No evidence quote on the record | The verdict was ungrounded and cannot fire strong | Usually a thin or badly extracted page |
corroboratingTier is unknown |
The source is not graded, and ungraded sources cap at weak | Add a trust rule for that domain |
| Two models consulted, alert still weak | The second opinion disagreed | Genuinely ambiguous evidence — consider a weak tier |
minSources: 2 set, one source had the news |
Corroboration not met | Add a second source, or drop minSources |
already-notified / semantic-duplicate
Section titled “already-notified / semantic-duplicate”Watcher believes it already told you. Check whether you actually received that earlier alert — if you did not, this is a delivery problem, not a detection one. Go to Alerts are not arriving.
no-candidates / collection-failed
Section titled “no-candidates / collection-failed”Nothing was collected at all. See Sources return nothing.
notify-failed
Section titled “notify-failed”Watcher decided correctly and could not deliver. The alert is queued and will be replayed automatically. See Alerts are not arriving.
The watch never ran
Section titled “The watch never ran”| Check | How |
|---|---|
| Is it enabled? | pnpm watches:list, or the toggle in the panel |
| Is it due? | Cron is UTC. A watch runs at the slower of its own cron and the instance’s schedule |
| Did the instance run? | Check your scheduled job’s logs, or the service logs |
| Was it imported? | Editing watches/*.yml does nothing until pnpm watches:import |
Test it immediately, bypassing all scheduling:
pnpm run-due:force