{"record":{"id":"dd9411bd1900f995","repo":"NousResearch/hermes-agent","slug":"isposix-install-sh-manifest-install-ps1-dd9411","errorCode":null,"errorMessage":"${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest'} produced no parseable JSON payload\\n${result.stdout}","messagePattern":"(.+?) produced no parseable JSON payload\\\\n(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/bootstrap-runner.ts","lineNumber":729,"sourceCode":"\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\n    }\n  }\n\n  throw new Error(\n    `${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest'} produced no parseable JSON payload\\n${result.stdout}`\n  )\n}\n\n// Parse the JSON result frame from a stage run. The protocol guarantees\n// exactly one JSON line per stage in -Json or -Stage mode (post #27224 fix\n// for the double-emit bug we addressed in the install.ps1 PR).\nfunction parseStageResult(stdout) {\n  const lines = 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 && typeof parsed.ok === 'boolean' && typeof parsed.stage === 'string') {\n        return parsed\n      }\n    } catch {","sourceCodeStart":711,"sourceCodeEnd":747,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/bootstrap-runner.ts#L711-L747","documentation":"ValueError from _validate_workdir (cron/jobs.py:1419): a job's workdir, after expanduser(), is not an absolute path. Because cron jobs run detached from any shell cwd, a relative path is ambiguous — the scheduler would resolve it against an arbitrary working directory. Both create_job and update_job enforce this via the same normalizer.","triggerScenarios":"Calling create_job(workdir='myproject') or update_job(job_id, {'workdir': './src'}); also '~/proj' is fine (expanduser runs first) but 'proj', './proj', '../proj' are rejected.","commonSituations":"An agent or script passes a path relative to the conversation cwd; a user copies a relative path out of docs; the gateway's terminal.cwd config is relative and someone mirrored it into a cron job.","solutions":["Pass an absolute path: create_job(..., workdir='/home/me/projects/myproject').","Expand at the call site from your current cwd: str(Path('myproject').resolve()).","Tilde paths like '~/proj' are accepted — expanduser() handles them.","To clear the workdir on update, pass None or '' rather than '.'."],"exampleFix":"# before\ncreate_job(prompt=\"run tests\", schedule=\"1h\", workdir=\"myproject\")\n\n# after\nfrom pathlib import Path\ncreate_job(prompt=\"run tests\", schedule=\"1h\", workdir=str(Path(\"myproject\").resolve()))","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef safe_workdir(w) -> str | None:\n    if w is None or not str(w).strip():\n        return None\n    p = Path(str(w).strip()).expanduser()\n    return str(p) if p.is_absolute() else str(p.resolve())  # make absolute up front","typeGuard":null,"tryCatchPattern":"try:\n    create_job(prompt=p, schedule=s, workdir=w)\nexcept ValueError as e:\n    if \"must be an absolute path\" in str(e):\n        create_job(prompt=p, schedule=s, workdir=str(Path(w).resolve()))  # retry absolute\n        raise","preventionTips":["Always build workdir with Path(...).resolve() at the call site.","Remember cron runs detached — there is no shell cwd to inherit, so relative paths are meaningless there."],"tags":["cron","workdir","paths","validation"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}