hermes - 💡(How to fix) Fix [Bug] `hermes cron create --profile <name>` switches HERMES_HOME instead of pinning — cron lands in target profile's jobs.json

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Per the cron docs (/docs/user-guide/features/cron § "Running cron jobs in a specific profile"):

By default a cron job inherits whichever Hermes profile owned the gateway / CLI that created it. Pass --profile <name> (CLI) or profile= (cronjob tool) to re-target the job at a different profile — the scheduler resolves that profile's HERMES_HOME, temporarily switches into it for the duration of the run, loads its .env + config.yaml, and executes the job there.

The natural reading is: cron lives in default's jobs.json with profile: "<name>" set as a pin, and the default gateway's scheduler ticks it but switches HERMES_HOME for the run only.

Observed behavior is opposite: the cron lands in <name>'s jobs.json with profile: null. The default gateway's scheduler doesn't see it. If the named profile's gateway isn't running (a common case in Pantheon-style "one Telegram bot" setups where secondary profile gateways are stopped), the cron never fires.

Error Message

  1. Keep current HERMES_HOME switch behavior but document it as such, and add a clear log warning at create time: WARN: cron created in profile 'oracle' but oracle's gateway is not running — this job will never fire.

Root Cause

Multi-profile Pantheon-style setups (where secondary profile gateways are deliberately stopped to avoid Telegram bot polling conflicts) silently lose every cron created with --profile. Easy to misdiagnose — cron list from the named profile shows the job as scheduled, but it never fires because no scheduler ticks for that profile.

Fix Action

Workaround

# 1. Create without --profile flag (lands in default's jobs.json)
hermes cron create --name oracle-nightly-dream "0 3 * * *" "..."
# 2. Edit jobs.json directly to set the pin
python3 -c "
import json
p = '/root/.hermes/cron/jobs.json'
d = json.load(open(p))
for j in d['jobs']:
    if j['name'] == 'oracle-nightly-dream':
        j['profile'] = 'oracle'
json.dump(d, open(p,'w'), indent=2)
"
# 3. Restart gateway so it picks up the pin
systemctl restart hermes-gateway

This works — the default gateway's scheduler now ticks the job and switches HERMES_HOME to oracle at fire time, as the docs describe.

The intended hermes cron edit <id> --profile oracle CLI path is broken with "Job not found" — filed separately as #32045.

Code Example

# Single Hermes install with a non-running secondary profile `oracle`
oracle gateway status   # → stopped

# Create from default CLI context, "pin" to oracle:
hermes cron create --name oracle-nightly-dream "0 3 * * *" "..." --profile oracle
# → Created job: f0467c04d70f
#   Next run: 2026-05-26T03:00:00-05:00

# It does NOT appear in default's list:
hermes cron list   # → empty (only the backup cron shows)

# It lives in oracle's jobs.json:
cat ~/.hermes/profiles/oracle/cron/jobs.json | jq '.jobs[]'
# → id: f0467c04d70f, profile: null   ← no pin set

# Oracle's gateway never runs, so this cron never fires.
# 24h later: last_run_at: null, next_run_at stuck in past.

---

# 1. Create without --profile flag (lands in default's jobs.json)
hermes cron create --name oracle-nightly-dream "0 3 * * *" "..."
# 2. Edit jobs.json directly to set the pin
python3 -c "
import json
p = '/root/.hermes/cron/jobs.json'
d = json.load(open(p))
for j in d['jobs']:
    if j['name'] == 'oracle-nightly-dream':
        j['profile'] = 'oracle'
json.dump(d, open(p,'w'), indent=2)
"
# 3. Restart gateway so it picks up the pin
systemctl restart hermes-gateway
RAW_BUFFERClick to expand / collapse

Summary

Per the cron docs (/docs/user-guide/features/cron § "Running cron jobs in a specific profile"):

By default a cron job inherits whichever Hermes profile owned the gateway / CLI that created it. Pass --profile <name> (CLI) or profile= (cronjob tool) to re-target the job at a different profile — the scheduler resolves that profile's HERMES_HOME, temporarily switches into it for the duration of the run, loads its .env + config.yaml, and executes the job there.

The natural reading is: cron lives in default's jobs.json with profile: "<name>" set as a pin, and the default gateway's scheduler ticks it but switches HERMES_HOME for the run only.

Observed behavior is opposite: the cron lands in <name>'s jobs.json with profile: null. The default gateway's scheduler doesn't see it. If the named profile's gateway isn't running (a common case in Pantheon-style "one Telegram bot" setups where secondary profile gateways are stopped), the cron never fires.

Repro

# Single Hermes install with a non-running secondary profile `oracle`
oracle gateway status   # → stopped

# Create from default CLI context, "pin" to oracle:
hermes cron create --name oracle-nightly-dream "0 3 * * *" "..." --profile oracle
# → Created job: f0467c04d70f
#   Next run: 2026-05-26T03:00:00-05:00

# It does NOT appear in default's list:
hermes cron list   # → empty (only the backup cron shows)

# It lives in oracle's jobs.json:
cat ~/.hermes/profiles/oracle/cron/jobs.json | jq '.jobs[]'
# → id: f0467c04d70f, profile: null   ← no pin set

# Oracle's gateway never runs, so this cron never fires.
# 24h later: last_run_at: null, next_run_at stuck in past.

The --profile <name> flag appears to switch the CLI's HERMES_HOME at create time (effectively oracle cron create ...), rather than pin the job's profile field.

Workaround

# 1. Create without --profile flag (lands in default's jobs.json)
hermes cron create --name oracle-nightly-dream "0 3 * * *" "..."
# 2. Edit jobs.json directly to set the pin
python3 -c "
import json
p = '/root/.hermes/cron/jobs.json'
d = json.load(open(p))
for j in d['jobs']:
    if j['name'] == 'oracle-nightly-dream':
        j['profile'] = 'oracle'
json.dump(d, open(p,'w'), indent=2)
"
# 3. Restart gateway so it picks up the pin
systemctl restart hermes-gateway

This works — the default gateway's scheduler now ticks the job and switches HERMES_HOME to oracle at fire time, as the docs describe.

The intended hermes cron edit <id> --profile oracle CLI path is broken with "Job not found" — filed separately as #32045.

Impact

Multi-profile Pantheon-style setups (where secondary profile gateways are deliberately stopped to avoid Telegram bot polling conflicts) silently lose every cron created with --profile. Easy to misdiagnose — cron list from the named profile shows the job as scheduled, but it never fires because no scheduler ticks for that profile.

Suggested resolution (pick one)

  1. Make --profile <name> set the profile pin field on a job created in default's jobs.json (matches the docs).
  2. Keep current HERMES_HOME switch behavior but document it as such, and add a clear log warning at create time: WARN: cron created in profile 'oracle' but oracle's gateway is not running — this job will never fire.

Option 1 is the user-visible promise from the docs.

Environment

  • Hermes installed via official curl ... | bash installer
  • Default profile = Ethel (Sonnet 4.6) with running gateway
  • Pantheon-style profiles (mercury, labyrinth, oracle) with gateways stopped
  • Debian 12 LXC on Proxmox
  • Verified on inherit_mcp_toolsets: true, profile clones via hermes profile create --clone

Related: #25290 (cron jobs split across profile directories — same root cause: profile context confusion at create time), #32045 (cron edit --profile returns "Job not found").

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING