✦
Hermes Agent

cron

AI Agent Cron Jobs: Failed Runs, Delivery, and Recovery

·AI agent cron jobscronautomationscheduled agentsmonitoring

Build and recover reliable AI agent cron jobs with Hermes: lifecycle states, run history, delivery errors, timezones, timeouts, provider safety, and verified reruns.

Hermes Agent cron jobs turn an AI assistant into scheduled operations: a morning brief, repository audit, quiet service monitor, customer-support digest, or publishing run that starts without a person opening chat. In Desktop Bot Mode, a Routine is the same underlying profile-scoped cron job, commonly named [bot:<name>] <routine>; inspect it in the Routines pane or hermes cron list, then verify run history and the explicit delivery target.

The schedule is the easy part. Reliable automation depends on five decisions: whether the job needs an LLM, which provider and tools it may use, which project directory it runs in, where the result is delivered, and what proves success.

Quick answer#

Use an agent cron job when each run needs judgment, research, writing, browser work, or tool use. Use script-only --no-agent mode when a deterministic script can produce the final alert itself. Cron runs in a fresh session, so make the prompt self-contained, set an absolute --workdir for repository jobs, choose an explicit delivery target, trigger one test run, and verify the actual message, file, commit, or live URL.

If this is your first Hermes workflow, complete the 15-minute setup smoke test before creating the schedule. Cron adds a fresh session, runtime uptime, timezone, provider, and delivery boundary; prove the underlying task once, then prove the scheduled outcome in the real destination.

For unattended spending safety, Hermes snapshots the provider/model selected when a job is created. If the global default later changes, an unpinned job fails closed instead of silently switching to the new model. The resolution order is per-job pin, then cron.model plus cron.model_provider, then the global default. Operators who deliberately want every unpinned job to follow later global changes can run hermes config set cron.model_drift_guard false, but that also removes the protection against an accidental switch to a paid model. Check the provider costs and rate-limit guide before changing this policy. For business-critical jobs that must survive a sleeping laptop, compare a 24/7 Hermes setup or the managed FlyHermes path.

What an AI agent cron job actually runs#

A Hermes cron job stores a schedule, prompt or script, delivery rule, and optional runtime controls. The gateway checks due jobs every minute and runs each due agent job in a fresh session. That isolation is deliberate: a scheduled run should not depend on whatever happened in an unrelated interactive chat.

A production job therefore needs:

  1. A supported schedule. Use a relative delay such as 30m, an interval such as every 2h, a five-field cron expression such as 0 9 * * 1-5, or an ISO timestamp.
  2. A self-contained task. Include source paths, URLs, required output, constraints, and the success condition.
  3. A runtime boundary. Set the project workdir, skills, toolsets, and model/provider deliberately.
  4. A delivery target. Send to local output, the origin chat, a home channel, a specific chat/topic, or multiple configured channels.
  5. A proof of completion. Verify the report, alert, file, test, commit, or production URL rather than trusting a green schedule row.

Ask Hermes conversationally to create the job, use /cron in chat, or use the standalone CLI. The schedule string accepted by the CLI is more precise than prose:

hermes cron create "0 9 * * 1-5" \
  "Review open pull requests, identify blockers, and send a brief with links." \
  --name "Weekday PR brief" \
  --deliver telegram \
  --workdir /absolute/path/to/repo

Then inspect and test it:

hermes cron list
hermes cron run "Weekday PR brief"
hermes cron status

run queues the job for the next scheduler tick. Confirm the gateway is healthy and inspect the destination rather than assuming the command means delivery succeeded.

Read the execution ledger, not only the job row#

A job row tells you what should run next. The execution ledger tells you what Hermes actually claimed and attempted. Inspect recent runs with:

hermes cron runs "Weekday PR brief" --limit 20

Each attempt moves through claimed and running to completed, failed, or unknown. An unknown attempt means Hermes recovered an abandoned run after restart; it is an audit record, not a run that Hermes silently retried. Compare the ledger with the real destination before deciding whether to rerun a job with side effects.

Choose the right execution mode#

Agent job: judgment is required#

Use a normal cron job when the output cannot be determined by a fixed script. Good examples include:

  • Synthesizing a daily research brief from several sources.
  • Prioritizing new GitHub issues by urgency.
  • Reviewing support conversations and drafting next actions.
  • Updating a content page, running QA, deploying it, and verifying production.
  • Explaining why a monitored page change matters.

The job gets the toolsets configured for the cron platform. A per-job enabled_toolsets setting can narrow that list further. This reduces both risk and tool-schema overhead: a news summary may need web and file access, but not terminal, browser, and delegation.

Attach reusable skills instead of stuffing the full procedure into every prompt:

hermes cron create "0 8 * * *" \
  "Find three relevant papers from the past day and save concise notes." \
  --skill arxiv \
  --skill obsidian \
  --name "Paper digest"

Script-only job: zero model calls#

Use --no-agent when the script itself can produce the exact message. Scripts must live under ~/.hermes/scripts/; pass the filename, not an arbitrary absolute path.

hermes cron create "every 5m" \
  --no-agent \
  --script memory-watchdog.sh \
  --deliver telegram \
  --name "Memory watchdog"

Script-only semantics are useful for real monitoring:

  • Non-empty stdout is delivered verbatim.
  • Empty stdout is a silent successful tick.
  • A non-zero exit or timeout produces an error alert.
  • No model, provider fallback, or token spend is involved.

For a hybrid job, attach a pre-check script without --no-agent. The script can collect deterministic data for the prompt. If its last line is {"wakeAgent": false}, Hermes skips the agent for that tick. This is the cost-efficient pattern for frequent polling where an LLM is only useful after a change.

For broader monitoring design, see AI agent monitoring and webhooks.

Make the future prompt self-contained#

Cron sessions do not inherit the current conversation. “Do the usual report” is not a safe prompt. Name the evidence, paths, decision rule, output, and failure behavior.

A stronger publishing prompt looks like this:

Work in /absolute/path/to/site.
Inspect the current content graph and today's evidence before choosing one page.
Ship one material improvement, run the repository's formatting, link, lint, and build checks,
commit and push the intended files, then verify the canonical production URL at phone and desktop widths.
Success means the live page contains the expected copy, has no horizontal overflow, and the final report includes the URL and test output.
If deployment or verification fails, report the exact failing layer; do not call the run successful.

That is a scheduled-work contract, not a reminder. If the output should be easy to continue tomorrow, use the AI agent session handoff checklist in the final report.

Set workdir for repository jobs#

Cron jobs are detached from a repository by default. They do not automatically load a project's AGENTS.md, CLAUDE.md, or .cursorrules, and file/terminal tools may start from the gateway's directory.

Set an absolute existing workdir:

hermes cron create "every 1d" \
  "Audit dependencies, run tests, and summarize only actionable changes." \
  --workdir /Users/me/projects/acme \
  --deliver local

With workdir set, Hermes injects supported project instruction files and points terminal, file, and code-execution tools at that directory. Workdir jobs run sequentially on a scheduler tick to prevent process-wide working-directory collisions. This is a reliability feature, but it also means several heavy repo jobs due at the same minute may wait for one another.

Let preflight block bad runs before they spend tokens#

Before constructing an agent run, Hermes validates the provider credential, attached skill requirements, and configured delivery target. A failed check sets last_status to blocked_config, delivers one alert, and makes no model call. Fix the missing key, skill dependency, or platform target, then trigger a manual run; the next healthy run clears the blocked state.

Keep preflight enabled unless you are diagnosing the validator itself. A job that cannot reach Slack or load its required skill should fail before research, browser work, or provider spend begins. Use hermes cron list, hermes cron runs <job> --limit 20, and the destination together: configuration state, execution state, and delivery proof are three different checks.

Pin provider behavior and control rate limits#

Provider failures are one of the most common reasons a schedule exists but no useful result arrives. Treat provider selection as part of the job definition.

At creation, Hermes snapshots the active global provider and model. If you later change the global default, an unpinned job does not silently follow it: the run is skipped and Hermes asks you to pin the provider/model explicitly. This prevents an unattended job from unexpectedly moving to a paid model.

Pin a job from the dashboard or with hermes cron create/edit --model … --provider …. For a fleet-wide default, set cron.model and cron.model_provider; unpinned jobs then use that route independently of interactive chat changes. A separate Hermes profile is another strong isolation boundary when scheduled work needs its own credentials and budget.

If a job is flooding failure messages while the chat provider is rate-limited, do not use /stop; manage it from a normal shell:

hermes cron list
hermes cron pause "Weekday PR brief"
# or permanently:
hermes cron remove "Weekday PR brief"

Configure credential pools or fallback providers only when you want that recovery behavior. Reduce toolsets, move deterministic checks into scripts, and lower frequency before adding expensive fallbacks. The provider costs and rate-limits guide explains API credits, OAuth limits, retries, and scheduled-job budgeting.

Deliver to the exact place#

CLI-created jobs default to local output, while jobs created from messaging platforms normally default to origin delivery. Origin is convenient for a test; it is fragile for a durable business workflow. Pin a concrete target instead:

  • slack sends to SLACK_HOME_CHANNEL.
  • slack:C0123456789 sends to one Slack channel by ID.
  • slack:#engineering can resolve a discovered named channel; a raw ID is safer for unattended work.
  • slack:U0123456789 opens that user's DM when the app has im:write.
  • telegram:-1001234567890:17585 sends to one Telegram forum topic.
  • discord:#ops targets a discovered Discord channel.
  • local saves output without messaging delivery.

For a Slack report to #engineering, invite the bot to the channel first, then create and test the job:

hermes cron create "0 9 * * 1-5" \
  "Summarize overnight incidents with source links and owners." \
  --name "Weekday incident brief" \
  --deliver slack:C0123456789

hermes cron run "Weekday incident brief"
hermes cron runs "Weekday incident brief" --limit 5

The Slack integration guide covers the bot token, app token, scopes, channel invitation, and home-channel setup. Hermes can deliver through its standalone Slack Web API sender even when the cron process is not co-located with the gateway, but the target still needs valid credentials and channel access.

Hermes also supports comma-separated fan-out such as telegram,discord and dynamic all delivery to configured home channels. The scheduler delivers the final response automatically; the cron prompt should not call a messaging tool to send the same answer again.

For Telegram topic mode, TELEGRAM_CRON_THREAD_ID can route normal cron delivery into a dedicated Cron topic. An explicit telegram:chat_id:thread_id target wins over that default. If routing fails, use the gateway troubleshooting guide and verify the exact chat, topic, Slack channel, or Discord thread rather than rotating a working token.

Make a delivery continuable when follow-up matters#

Cron delivery is fire-and-forget by default. If you want to reply “do task two” and have Hermes understand the delivered brief, enable a continuable job with attach_to_session through the cronjob tool, or set cron.mirror_delivery: true globally.

Thread-capable platforms prefer a fresh thread for each run, keeping follow-up conversations isolated. Broadcast fan-out targets are not made continuable. Use this for daily decision briefs; leave it off for one-way alerts.

Keep quiet jobs quiet#

For agent jobs, a successful final response containing [SILENT] suppresses delivery while preserving local output for audit. Failed jobs still alert. A monitor prompt can say:

Check the service. If it is healthy, respond with exactly [SILENT].
Otherwise report the failing check, evidence, and first recovery command.

For deterministic checks, script-only mode is better: empty stdout already means silence and costs zero tokens.

Chain jobs without pretending they share memory#

Cron jobs are isolated, but context_from can prepend the most recent output from one or more upstream jobs to a downstream prompt. This supports collect → rank → publish pipelines without pretending separate sessions share conversational memory.

Use it when each stage has a clear artifact and can fail independently. Keep schedules far enough apart for the upstream job to finish, and make each stage idempotent. For valuable data, persist the canonical artifact to a file or database too; an output handoff should not be the only copy.

Diagnose failed runs by state, not guesswork#

When an AI agent cron job appears to fail, first identify which layer failed. A schedule row, an execution attempt, and a delivered message are different records. Treating them as one status leads to unsafe reruns and duplicate side effects.

Use this order:

hermes gateway status
hermes cron status
hermes cron list
hermes cron runs "Job name" --limit 20

Then inspect the exact delivery destination and any artifact the task was supposed to create. The useful state map is:

  • scheduled: the job is active and has a future next_run_at.
  • paused: the definition remains, but the scheduler will not fire it.
  • completed: a one-shot fired or a finite repeat count was exhausted. This is not the same as a recurring job being healthy.
  • blocked_config: preflight found a provider, skill, or delivery configuration problem before inference. Hermes alerts once and avoids the model call.
  • failed in run history: execution was claimed but did not complete successfully.
  • unknown in run history: a restart left an abandoned attempt whose original process is gone. It is retained for audit and is not automatically rerun.
  • last_delivery_error: the work may have completed, but the output did not reach every intended destination.
  • No delivered message after a successful run: confirm whether the job emitted no output or exactly [SILENT]; quiet success is intentional for monitors.

This distinction matters for jobs that publish, charge, delete, or modify production. If the execution state is uncertain, reconcile the external target before using hermes cron run again. A manual run is a new attempt, not an exactly-once replay.

Recover without making the incident worse#

For a recurring job, pause first when it is noisy or consequential:

hermes cron pause "Job name"
hermes cron runs "Job name" --limit 20
# fix the provider, workdir, script, prompt, or delivery target
hermes cron run "Job name"
# verify the real destination or artifact, then:
hermes cron resume "Job name"

Recurring jobs track a failure_streak. After the configured threshold (three by default), Hermes adds a review nudge; a successful execution resets the streak. Delivery failures are tracked separately and do not inflate the execution streak. This prevents a broken Slack or Telegram route from being misdiagnosed as failed research or code execution.

Fix timing, repeats, and stopped-host surprises#

Hermes cron expressions use the scheduler host's local timezone. 0 9 * * * means 9:00 AM where that host is configured—not necessarily the timezone of the phone reading the result. Compare the host clock with next_run_at, and encode the operating timezone in the prompt when report dates or market windows depend on it.

Relative schedules such as 30m and ISO timestamps are one-shot jobs. Intervals such as every 2h and five-field cron expressions recur. If a job ran once and now shows completed, inspect the schedule and finite repeat count before assuming the scheduler lost it.

The gateway owns automatic ticks. A normal CLI chat does not keep cron alive, and a sleeping laptop or intentionally stopped server cannot execute a local schedule. For durable self-hosting, install the gateway service and test through a reboot. For an operationally critical report, compare the self-hosted versus hosted AI agent checklist rather than assuming a saved schedule guarantees uptime.

Put timeouts around the correct phase#

A long job can fail in four different time budgets:

  1. Agent inactivity: the default cron inactivity timeout is 600 seconds. Active tool calls may keep a run alive; a stalled provider or idle agent does not. Configure HERMES_CRON_TIMEOUT only after diagnosing the stall, and avoid unlimited timeouts for unattended work.
  2. Pre-run script: cron.script_timeout_seconds defaults to 3,600 seconds. Filter large collector output before it enters model context.
  3. Media delivery: cron.media_send_timeout_seconds defaults to 300 seconds per attachment. Text can arrive while a slow attachment records a partial delivery failure.
  4. Bot Chat delivery: a Bot Chat target launches another full agent turn and has its own cron.bot_chat_delivery_timeout_seconds budget, 600 seconds by default. A timed-out delivery can still finish later, so reconcile before retrying.

Do not raise every timeout as the first fix. Reduce unnecessary toolsets, move deterministic collection into a script, and make the output bounded. The monitoring and webhook guide shows how to alert on failure without creating another expensive loop.

Pin reasoning effort per scheduled workload#

A daily synthesis and a five-minute classifier do not need the same thinking budget. Pin reasoning independently of the interactive session:

hermes cron edit "Weekly architecture review" --reasoning-effort high
hermes cron edit "Routine feed classifier" --reasoning-effort minimal

Supported levels run from none through minimal, low, medium, high, xhigh, max, and ultra; providers clamp unsupported levels. The pin has no effect on script-only jobs because no model is called. Like model/provider pins, per-job reasoning is deliberately user-owned and is not changed by an unattended cron agent. This gives routine jobs a predictable speed/cost envelope while reserving deeper reasoning for work that benefits from it.

Production checklist#

Before relying on a scheduled agent:

  • Run the underlying task once interactively.
  • Use a supported schedule string and confirm the intended timezone.
  • Make the prompt self-contained.
  • Choose agent, hybrid pre-check, or script-only mode.
  • Set an absolute workdir for project jobs.
  • Attach only the required skills and toolsets.
  • Confirm the provider/model snapshot and expected fallback policy.
  • Pin a specific delivery destination.
  • Trigger one manual run and inspect the real output.
  • Check hermes cron status and gateway health.
  • Define how to pause the job during provider or delivery failures.
  • For publishing, require tests, push/deploy, live URL, and rendered phone/desktop QA.

The Hermes Dashboard and Web UI is useful for inspecting cron, profile, tool, provider, and gateway state. It is not proof that the outcome happened. The proof is the delivered brief, saved file, passing test, alert, commit, or live page.

Self-hosted cron or managed uptime?#

Self-hosting gives you full control over providers, scripts, filesystem access, and gateway routing. It also makes you responsible for the machine staying awake, service restarts, updates, backups, credentials, monitoring, and delivery recovery.

If that control is the point, follow the self-hosted versus hosted AI agent guide. If scheduled work is business-critical and you do not want a VPS, Docker, and gateway maintenance project, FlyHermes is the managed path for browser/mobile access, connected channels, and uptime.

Give unattended jobs a cost ceiling#

Scheduled work can retry while nobody is watching. Set provider spending alerts, cap runtime and retries, stagger jobs sharing one provider, and require an explicit output artifact before marking success. Pin important jobs to a tested provider route so a global model change cannot silently move them onto an empty wallet. Use the provider cost and rate-limit checklist when a cron run ends with 402 or 429.

FAQ#

Do Hermes cron jobs remember the chat that created them?#

No. Agent jobs start in fresh sessions. Put durable procedures in skills and include paths, sources, constraints, output format, and success criteria in the job prompt.

Can a cron job run without an LLM?#

Yes. Use --no-agent --script filename for a script under ~/.hermes/scripts/. Non-empty stdout is delivered, empty stdout is silent, and failures alert.

Why did a cron job stop after I changed models?#

Hermes snapshots the provider/model when a job is created and fails closed after an unexpected global model change. Ask Hermes to pin the job's provider/model explicitly, then trigger a test run.

Can one job deliver to Telegram and Discord?#

Yes. Use a comma-separated target such as telegram,discord, or all for configured home channels. Use exact chat/topic/channel targets for important workflows.

Why did the job run in the wrong repository?#

Repository context is not automatic. Set an absolute --workdir; then verify the project instruction files and tool working directory during a manual run.

Can I reply to a cron report and continue the task?#

Only when continuable delivery is enabled with attach_to_session for the job or cron.mirror_delivery: true globally. Otherwise cron delivery is fire-and-forget.

What should count as success?#

The outcome, not the schedule: a message delivered to the intended topic, a saved artifact, a passing test, a verified quiet tick, a pushed commit, or a live URL that passed rendered QA.

Browser jobs need stronger success evidence#

A scheduled browser run should not report success merely because navigation started. Require the expected page state or created record, a console check when JavaScript matters, a screenshot path, bounded retries, and delivery into the intended channel. The browser automation troubleshooting guide covers expired sessions, 502s, Docker-localhost mistakes, and anti-bot boundaries.

A schedule cannot wake an offline Hermes runtime#

A correct cron expression does not make a laptop-hosted agent always-on. The machine and Hermes scheduler service must be awake, the active profile must have provider credentials, and the gateway must be able to reach the pinned delivery target. Test one harmless job while Desktop is closed and the client laptop is not assisting the runtime. Use the self-hosted vs hosted AI agent guide when scheduled work must survive laptop sleep.

Put a spend preflight in every LLM-driven job#

Before an unattended run, verify the pinned provider/model, current credit state, external spending ceiling, expected subagent concurrency, retry cap, and delivery target. Keep the model-drift guard unless following global model changes is intentional. Fresh August support evidence shows that an overnight job can become expensive when the active route differs from the operator's assumption. The provider cost incident checklist explains how to stop work, preserve evidence, and re-enable schedules safely.

Find scheduled runs under Automation, not only Chats#

The Hermes Web UI Sessions page defaults to human Chats and hides automation noise. If a cron run looks missing, switch to Automation or All, select the owning profile, and filter by source before declaring the scheduler broken. Then verify the configured delivery target and the real artifact or message. If scheduled work must stay available without dashboard, VPS, provider, and gateway upkeep, compare FlyHermes pricing.

Do not let cron choose a browser identity implicitly#

For authenticated browser jobs, pin the Hermes profile and the browser identity before scheduling. A machine with several Chromium profiles can change last_used between runs; use browser.real_profile_pin when the job relies on a real-profile snapshot. Test the browser workflow read-only, then verify the cron's explicit delivery target.

Deliver cron reports into a Telegram topic#

In a topic-enabled Telegram DM, the root is a system lobby. Set TELEGRAM_CRON_THREAD_ID, run one job manually, and verify the message in that exact topic. The Telegram setup guide covers /sethome, topic isolation, and why a parent-chat test is insufficient.

v0.21 adds continuity, not exactly-once execution#

Pantheon adds persistent memory, continuity=true, durable job notepads, monitor-mode no-change skipping, and canonical Bot Chat delivery. The v0.21 release guide explains the new surface; this recovery guide remains the owner for lifecycle, delivery, timeout, and safe-rerun decisions.

Watch the run in Dashboard without confusing visibility with success#

The Hermes Dashboard can show cron configuration and Kanban/worker state, while Automation and All expose non-chat sessions. Use those views to find the owning profile and error, but keep the success test outside the dashboard: the expected message, file, deployment, or webhook must arrive. FlyHermes is the managed path when keeping the scheduler, gateway, backend, and delivery route online is not work you want to own.

For scheduled browser jobs, a successful one-off navigation is not enough. Test the same named browser lane after a runtime restart and require final URL, screenshot, console, artifact, and delivery evidence. See the browser automation recovery guide.

Frequently Asked Questions

What is an AI agent cron job?

It is a scheduled agent or script run that performs work automatically and produces a result such as a brief, alert, file, test, commit, or published page.

Do Hermes cron jobs remember the chat that created them?

No. Agent jobs start in fresh sessions, so the prompt must include the task, paths, sources, constraints, output format, and success criteria.

Can a Hermes cron job run without an LLM?

Yes. Use no-agent mode with a script under ~/.hermes/scripts/. Non-empty stdout is delivered, empty stdout is silent, and a non-zero exit or timeout produces an error alert.

Why did my cron job stop after I changed models?

Hermes snapshots the provider and model at job creation and fails closed after an unexpected global default change. Pin the intended provider/model explicitly and trigger a test run.

Can a Hermes cron job deliver to a specific Slack channel?

Yes. Use slack:C0123456789 for a raw Slack channel ID, slack:#engineering for a discovered named channel, or bare slack for the configured home channel. Invite the bot and verify one real delivery.

Can I reply to a Hermes cron report?

Cron reports are fire-and-forget by default. Enable attach_to_session for that job, or cron.mirror_delivery globally, to make supported deliveries continuable.

Are Bot Mode routines different from Hermes cron jobs?

No. A Bot Routine is a profile-scoped Hermes cron job, commonly named with a `[bot:<name>]` prefix. It appears in the Desktop Routines pane and `hermes cron list`; use cron run history and the configured destination to verify the result.

Why is my cron run missing from Hermes dashboard Sessions?

Sessions defaults to Chats. Switch to Automation or All, select the owning profile, and filter by source. Then compare the run record with the actual delivery destination.

Why does a Hermes cron job show completed instead of scheduled?

A one-shot schedule has fired or a finite repeat count was exhausted. Inspect the schedule and repeat settings before recreating it; completed is a lifecycle state, not proof that a recurring schedule remains armed.

What does blocked_config mean for a Hermes cron job?

Preflight found a missing provider credential, unavailable skill requirement, or invalid delivery configuration before starting inference. Hermes alerts once and avoids spending a model call until the configuration is healthy.

Should I rerun a cron job marked unknown?

Not until you reconcile the external target. Unknown means a restart abandoned the recorded attempt; it is not automatically replayed. Check files, messages, commits, payments, or other side effects before triggering a new attempt.

Which timezone does Hermes cron use?

Cron expressions use the scheduler host's local timezone. Compare the host clock and next_run_at, and state the operating timezone inside time-sensitive report prompts.

Can I set reasoning effort for one cron job?

Yes. Use hermes cron create or edit with --reasoning-effort. The per-job pin is independent of the chat setting and has no effect on no-agent script-only jobs.

FlyHermes (Managed Cloud)

Deploy in 60 seconds. API costs included. Cancel anytime.

Deploy faster with FlyHermes →

Self-Host (Open Source)

Full control. MIT licensed. Run on your own infrastructure.

View install guide →

Keep reading

Related Hermes Agent guides