NousResearch/hermes-agent · error

Desktop installation ID path is not a regular file.

Error message

Desktop installation ID path is not a regular file.

What it means

ValueError from update_job (cron/jobs.py:1972): after applying updates, the job is enabled and not paused but has no next_run_at, so it recomputes — and for a 'once' schedule compute_next_run returns None (run_at is past the 120s grace window). This arm catches updates that didn't touch the schedule at all but re-enabled an expired one-shot job (e.g. flipping enabled=True).

Source

Thrown at apps/desktop/electron/desktop-installation.ts:92

      }

      waitForRepair()

      continue
    }

    try {
      const winner = readInstallationId(filePath)

      if (winner) {
        return winner
      }

      try {
        const stat = fs.lstatSync(filePath)

        if (!stat.isFile() && !stat.isSymbolicLink()) {
          throw new Error('Desktop installation ID path is not a regular file.')
        }

        if (!stat.isSymbolicLink() && typeof process.getuid === 'function' && stat.uid !== process.getuid()) {
          throw new Error('Desktop installation ID is owned by another user.')
        }

        fs.unlinkSync(filePath)
      } catch (error: any) {
        if (error?.code !== 'ENOENT') {
          throw error
        }
      }

      fs.writeFileSync(filePath, JSON.stringify({ installationId }), { encoding: 'utf8', flag: 'wx', mode: 0o600 })

      return installationId
    } finally {
      if (repairFd !== undefined) {

View on GitHub (pinned to c896c09c42)

Solutions

  1. Give the job a fresh future schedule in the same update: {'enabled': True, 'schedule': '1h'}.
  2. For a rerun, create a new one-shot job instead of reviving the expired one.
  3. Use resume_job for paused jobs — it raises the dedicated 'Cannot resume' error with clearer intent.

Example fix

# before
update_job(job_id, {"enabled": True})  # one-shot already expired

# after
update_job(job_id, {"enabled": True, "schedule": "30m"})
Defensive patterns

Strategy: try-catch

Validate before calling

from cron.jobs import load_jobs, compute_next_run
from datetime import datetime

def can_reenable(job: dict) -> bool:
    if job["schedule"].get("kind") != "once":
        return True
    return compute_next_run(job["schedule"]) is not None  # None = expired one-shot

Try / catch

try:
    update_job(job_id, {"enabled": True})
except ValueError as e:
    if "grace window" in str(e):  # expired one-shot re-enabled
        update_job(job_id, {"enabled": True, "schedule": "1h"})  # fresh schedule too
        raise

Prevention

When it happens

Trigger: update_job(id, {'enabled': True}) or {'state': 'scheduled'} on a one-shot job whose run_at already passed while it was disabled/paused; clearing next_run_at via an update.

Common situations: Re-enabling an old one-shot job after its fire time; un-pausing via raw update instead of resume_job; bulk 'enable all' scripts that wake expired one-shots.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/74824b0242ed4f4d. Report an issue: GitHub.