{"record":{"id":"55bdce89b631bbea","repo":"NousResearch/hermes-agent","slug":"isposix-install-sh-manifest-install-ps1","errorCode":null,"errorMessage":"${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest'} failed: exit ${result.code}\\n${result.stderr || result.stdout}","messagePattern":"(.+?) failed: exit (.+?)\\\\n(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"apps/desktop/electron/bootstrap-runner.ts","lineNumber":707,"sourceCode":"\n  return args\n}\n\nasync function fetchManifest({ scriptPath, installerKind, emit, hermesHome, activeRoot, installStamp, pinCommit }) {\n  const isPosix = installerKind === 'posix'\n\n  const args = isPosix\n    ? ['--manifest', ...buildPosixPinArgs({ installStamp, activeRoot, hermesHome, pinCommit })]\n    : ['-Manifest', ...buildPinArgs(installStamp, { pinCommit })]\n\n  const result = await (isPosix ? spawnBash : spawnPowerShell)(scriptPath, args, {\n    emit,\n    stageName: '__manifest__',\n    hermesHome\n  })\n\n  if (result.code !== 0) {\n    throw new Error(\n      `${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest'} failed: exit ${result.code}\\n${result.stderr || result.stdout}`\n    )\n  }\n\n  // The manifest is the LAST JSON line on stdout (install.ps1 may print\n  // banner / info lines first depending on Console.OutputEncoding effects).\n  // Find the last line that parses as JSON with a `stages` field.\n  const lines = result.stdout.split(/\\r?\\n/).filter(Boolean)\n\n  for (let i = lines.length - 1; i >= 0; i--) {\n    try {\n      const parsed = JSON.parse(lines[i])\n\n      if (parsed && Array.isArray(parsed.stages)) {\n        return parsed\n      }\n    } catch {\n      void 0","sourceCodeStart":689,"sourceCodeEnd":725,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/bootstrap-runner.ts#L689-L725","documentation":"RuntimeError from load_jobs() (cron/jobs.py:1120): jobs.json parsed successfully as JSON, but the top-level value is neither an object (expected {'jobs': [...]}) nor a list (auto-repairable) — it is a string, number, bool, or null. Without this guard, data.get('jobs') would raise AttributeError and take down the cron subsystem; instead it fails with a precise type name.","triggerScenarios":"jobs.json contains e.g. \"ok\", 42, true, or null at top level — usually a script or human overwriting the file with something that isn't a job store at all.","commonSituations":"A user echoed a status string into jobs.json by mistake; a monitoring script truncated the file to empty content that parsed as null; an editor auto-saved a scratch note into the wrong file.","solutions":["Look at the reported type name in the message to confirm what's actually in the file.","Back up the file, then replace its contents with {\"jobs\": []}.","Recreate needed jobs with `hermes cron add` (or restore from a backup of the old JSON).","Ensure only Hermes writes jobs.json — no shell redirects or editors target it."],"exampleFix":"# before: ~/.hermes/cron/jobs.json contains  null\necho '{\"jobs\": []}' > ~/.hermes/cron/jobs.json  # after\nhermes cron list","handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\nfrom hermes_constants import get_hermes_home\n\ndef jobs_payload_ok() -> bool:\n    f = get_hermes_home() / \"cron\" / \"jobs.json\"\n    if not f.exists():\n        return True\n    data = json.loads(f.read_text())\n    return isinstance(data, (dict, list)) and not isinstance(data, (str, bytes))\n\n# a bare string/number/null top level will fail here before Hermes raises","typeGuard":"def is_jobs_payload(data: object) -> bool:\n    \"\"\"True when data is a shape load_jobs() accepts or auto-repairs.\"\"\"\n    return isinstance(data, dict) or isinstance(data, list)","tryCatchPattern":"try:\n    jobs = list_jobs()\nexcept RuntimeError as e:\n    if \"expected {'jobs\": \" in str(e) or \"got \" in str(e) and \"corrupted\" in str(e):\n        f.write_text('{\"jobs\": []}')  # after backing up; then reload\n        jobs = list_jobs()","preventionTips":["Only Hermes writes jobs.json — no shell redirects, editors, or monitoring scripts targeting it.","If you must inspect it, read-only via `hermes cron list` or python -m json.tool."],"tags":["cron","json","corruption","runtimeerror"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}