{"record":{"id":"74824b0242ed4f4d","repo":"NousResearch/hermes-agent","slug":"desktop-installation-id-path-is-not-a-regular-file","errorCode":null,"errorMessage":"Desktop installation ID path is not a regular file.","messagePattern":"Desktop installation ID path is not a regular file\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/desktop-installation.ts","lineNumber":92,"sourceCode":"      }\n\n      waitForRepair()\n\n      continue\n    }\n\n    try {\n      const winner = readInstallationId(filePath)\n\n      if (winner) {\n        return winner\n      }\n\n      try {\n        const stat = fs.lstatSync(filePath)\n\n        if (!stat.isFile() && !stat.isSymbolicLink()) {\n          throw new Error('Desktop installation ID path is not a regular file.')\n        }\n\n        if (!stat.isSymbolicLink() && typeof process.getuid === 'function' && stat.uid !== process.getuid()) {\n          throw new Error('Desktop installation ID is owned by another user.')\n        }\n\n        fs.unlinkSync(filePath)\n      } catch (error: any) {\n        if (error?.code !== 'ENOENT') {\n          throw error\n        }\n      }\n\n      fs.writeFileSync(filePath, JSON.stringify({ installationId }), { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n\n      return installationId\n    } finally {\n      if (repairFd !== undefined) {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/desktop-installation.ts#L74-L110","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Give the job a fresh future schedule in the same update: {'enabled': True, 'schedule': '1h'}.","For a rerun, create a new one-shot job instead of reviving the expired one.","Use resume_job for paused jobs — it raises the dedicated 'Cannot resume' error with clearer intent."],"exampleFix":"# before\nupdate_job(job_id, {\"enabled\": True})  # one-shot already expired\n\n# after\nupdate_job(job_id, {\"enabled\": True, \"schedule\": \"30m\"})","handlingStrategy":"try-catch","validationCode":"from cron.jobs import load_jobs, compute_next_run\nfrom datetime import datetime\n\ndef can_reenable(job: dict) -> bool:\n    if job[\"schedule\"].get(\"kind\") != \"once\":\n        return True\n    return compute_next_run(job[\"schedule\"]) is not None  # None = expired one-shot","typeGuard":null,"tryCatchPattern":"try:\n    update_job(job_id, {\"enabled\": True})\nexcept ValueError as e:\n    if \"grace window\" in str(e):  # expired one-shot re-enabled\n        update_job(job_id, {\"enabled\": True, \"schedule\": \"1h\"})  # fresh schedule too\n        raise","preventionTips":["Before bulk-enabling jobs, filter out expired one-shots or refresh their schedules in the same update.","Use resume_job for pause/resume flows — its error message is the most actionable."],"tags":["cron","one-shot","update","schedule"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}