NousResearch/hermes-agent · error
Timed out connecting to Hermes backend after ${timeoutMs}ms
Error message
Timed out connecting to Hermes backend after ${timeoutMs}ms What it means
ValueError from create_job (cron/jobs.py:1732): a one-shot ('once') schedule resolved, but compute_next_run() returned None — the requested run_at is more than ONESHOT_GRACE_SECONDS (120s) in the past, so the job would never fire and is rejected at creation instead of sitting dead in the store. A warning is logged alongside.
Source
Thrown at apps/desktop/electron/dashboard-token.ts:23
* spawns the local dashboard, but the dashboard is the source of truth for the
* token it actually serves to the renderer. If those drift, HTTP readiness
* probes still pass while /api/ws rejects the renderer's token.
*/
const DEFAULT_TOKEN_FETCH_TIMEOUT_MS = 3_000
async function fetchPublicText(url, options: any = {}) {
const { protocol } = new URL(url)
if (protocol !== 'http:' && protocol !== 'https:') {
throw new Error(`Unsupported Hermes backend URL protocol: ${protocol}`)
}
const timeoutMs = options.timeoutMs ?? DEFAULT_TOKEN_FETCH_TIMEOUT_MS
const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) }).catch(error => {
if (error.name === 'TimeoutError') {
throw new Error(`Timed out connecting to Hermes backend after ${timeoutMs}ms`)
}
throw error
})
const text = await res.text()
if (!res.ok) {
throw new Error(`${res.status}: ${text || res.statusText}`)
}
return text
}
function extractInjectedDashboardToken(html) {
const match = /window\.__HERMES_SESSION_TOKEN__\s*=\s*("(?:\\.|[^"\\])*")/.exec(String(html || ''))
if (!match) {View on GitHub (pinned to c896c09c42)
Solutions
- Pick a future timestamp, or use a relative duration ('30m', '2h') which is computed from now.
- Check the clock and timezone on the host running the cron scheduler — the grace window is only 120s.
- For recurring work, use cron/interval schedules instead of one-shot timestamps.
Example fix
# before (at 15:00) create_job(prompt="report", schedule="2026-08-14T09:00") # after create_job(prompt="report", schedule="2026-08-14T16:00") # or "2h"
Defensive patterns
Strategy: validation
Validate before calling
from datetime import datetime, timedelta
from cron.jobs import ONESHOT_GRACE_SECONDS
def schedulable_one_shot(when: datetime) -> bool:
return when > datetime.now(when.tzinfo) + timedelta(seconds=ONESHOT_GRACE_SECONDS - 10)
# or skip absolute times entirely and use a duration string: "45m" Try / catch
try:
job = create_job(prompt=p, schedule="2026-08-14T09:00")
except ValueError as e:
if "in the past" in str(e):
job = create_job(prompt=p, schedule="1h") # retry relative to now
raise Prevention
- Prefer relative durations for one-shots created programmatically — they can't be born in the past.
- Compare timestamps against the scheduler host's clock/timezone, especially across SSH/containers.
When it happens
Trigger: create_job with an ISO timestamp like '2025-01-01T09:00' (already past), or a duration so small the time elapsed between parse and validation; also clock skew where the scheduler host's clock is ahead of the client's.
Common situations: User schedules 'today at 9am' after 9am; timestamps built from a stale clock; jobs created on a machine whose timezone/clock differs from the gateway host; DST confusion making a 'future' local time actually past.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Could not generate a valid desktop installation ID.
- Preview mode — launching is disabled.
- no install root
- Desktop installation ID path is not a regular file.
- Desktop installation ID is owned by another user.
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/374f6153031aaf09.
Report an issue: GitHub.