NousResearch/hermes-agent · error
Could not generate a valid desktop installation ID.
Error message
Could not generate a valid desktop installation ID.
What it means
ValueError from update_job (cron/jobs.py:1952): the update changed the job's schedule to a 'once' kind whose run_at is more than ONESHOT_GRACE_SECONDS (120s) in the past (compute_next_run returned None). Same guard as creation, applied through the update door so an existing job can't be pushed into a state where it would never fire.
Source
Thrown at apps/desktop/electron/desktop-installation.ts:55
}
function waitForRepair() {
const buffer = new SharedArrayBuffer(4)
Atomics.wait(new Int32Array(buffer), 0, 0, 25)
}
function loadOrCreateInstallationId(filePath, randomUUID = crypto.randomUUID) {
const existing = readInstallationId(filePath)
if (existing) {
return existing
}
fs.mkdirSync(path.dirname(filePath), { recursive: true })
const installationId = randomUUID().toLowerCase()
if (!INSTALLATION_ID_RE.test(installationId)) {
throw new Error('Could not generate a valid desktop installation ID.')
}
const repairPath = `${filePath}.repair.lock`
for (let attempt = 0; attempt < 40; attempt++) {
let repairFd
try {
repairFd = fs.openSync(repairPath, 'wx', 0o600)
} catch (error: any) {
if (error?.code !== 'EEXIST') {
throw error
}
const winner = readInstallationId(filePath)
if (winner) {
return winnerView on GitHub (pinned to c896c09c42)
Solutions
- Set the new schedule to a future timestamp or a relative duration like '45m'.
- If you meant to reschedule a missed run, compute now + delta rather than reusing the old time.
- Verify the scheduler host's clock/timezone if the timestamp 'looks future' to you.
Example fix
# before
update_job(job_id, {"schedule": "2026-08-14T09:00"}) # past
# after
update_job(job_id, {"schedule": "2h"}) # one-shot two hours from now Defensive patterns
Strategy: validation
Validate before calling
from datetime import datetime, timedelta
def future_schedule(s: str) -> bool:
if not s or s[0].isdigit() and "T" in s:
try:
return datetime.fromisoformat(s) > datetime.now() + timedelta(minutes=5)
except ValueError:
return False
return True # durations/intervals/cron are computed from now Try / catch
try:
update_job(job_id, {"schedule": new_sched})
except ValueError as e:
if "in the past" in str(e):
update_job(job_id, {"schedule": "1h"}) # reschedule relative to now
raise Prevention
- When rescheduling a missed one-shot, compute now + delta instead of reusing the old timestamp.
- Combine schedule changes with enabled/state changes in one update so all guards see a consistent job.
When it happens
Trigger: update_job(id, {'schedule': '2026-01-01T09:00'}) with a past timestamp; editing the schedule of a paused one-shot job whose time has since passed; clock skew between the client and scheduler host.
Common situations: Postponing a missed one-shot by nudging the schedule but picking an already-elapsed time; copy-pasting an old timestamp; editing on a machine whose clock/timezone differs from the gateway.
Related errors
- Timed out connecting to Hermes backend after ${timeoutMs}ms
- Desktop installation ID path is not a regular file.
- Desktop installation ID is owned by another user.
- Preview mode — launching is disabled.
- no install root
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/c37843dc0e7744fb.
Report an issue: GitHub.