Field guide
Notice when a scheduled agent never starts.
Configure an expected cron schedule, timezone, grace period and runtime limit for a recurring Python agent while keeping your existing scheduler.
Code and examples reviewed
A scheduled-agent monitor compares expected time slots with received run starts. It can raise a missed-start incident even when the agent emitted nothing. The expectation must exist before the missed run: Watchdog does not start your original job.
1. Keep one scheduler responsible for launching.
Continue using your existing cron service, worker scheduler or container platform. Suppose it starts a report at 07:00 UTC each day. Register the same five-field cron expression and timezone in Watchdog, and allow five minutes of start jitter.
{
"name": "Daily report",
"slug": "daily-report",
"schedule": "0 7 * * *",
"timezone": "UTC",
"config": {
"grace_period": 300,
"max_runtime": 900,
"progress_timeout": 1800,
"auto_cancel": false,
"auto_retry": false,
"max_retries": 0
}
}Enter these values in Register a job. The JSON names correspond to the existing job API; the administrator can also use it through an authenticated request. It is configuration, not a command that launches the report.
2. Instrument the launched script.
After the SDK setup, use the same registered slug:
from watchdog_agent import Watchdog
def main():
with Watchdog() as watchdog:
with watchdog.run("daily-report", cancellable=False):
# Replace this with the actual job after testing delivery.
print("Scheduled synthetic report finished")
if not watchdog.flush(timeout=10):
raise RuntimeError("Telemetry did not drain")
if __name__ == "__main__":
main()Your scheduler invokes this script. Create a fresh SDK client after a worker process forks. A long-running job can emit meaningful progress; the example deliberately uses a progress timeout longer than the runtime limit so lack of progress is not its first incident.
3. Interpret the timeline.
| Situation | Expected interpretation |
|---|---|
| Start at 07:02 UTC | Within the five-minute start grace period. |
| No start after 07:05 UTC | Eligible for missed-start detection on a subsequent independent check. |
| Started at 07:02, still active after 07:17 | Eligible for the fifteen-minute runtime limit check. |
| On-time start arrives late as telemetry | The received timestamped event can reconcile a missed-start incident. |
The hosted deployment uses a native minute Cron Trigger to check expectations, not to execute the customer job. Detection and delivery include scheduler cadence, workload backlog and network time. Do not promise an alert at exactly the deadline. Cloudflare documents its Cron Trigger behavior.
Use an explicit timezone.
Watchdog uses IANA timezone names. UTC avoids local daylight-saving ambiguity; if the business requirement is a local hour, configure that local zone in both the launcher and Watchdog. The monitor persists unique UTC occurrence slots. Inspect the next expected execution before enabling the job and test dates around daylight-saving changes.
A five-field expression describes expected slots; it is not a universal scheduler contract. Do not assume an early run will satisfy a later slot or that every external scheduler shares identical catch-up semantics.
4. Test a miss without disrupting production.
Create a separate synthetic job with its own schedule and no real business task. Enable an alert destination you control. Deliberately omit that test job’s start, keep the dashboard closed and verify both the missed-start incident and the received alert. Then pause the test job. Do not disable a production launcher for this exercise.
The four-case Failure Lab covers a started-but-silent run, not a missed-start scenario. The repository’s existing detector tests cover scheduled grace, deduplication, timezones and delayed-start reconciliation. Keep those types of evidence distinct.