NousResearch/hermes-agent · error
${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest
Error message
${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest'} produced no parseable JSON payload\n${result.stdout} What it means
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.
Source
Thrown at apps/desktop/electron/bootstrap-runner.ts:729
// The manifest is the LAST JSON line on stdout (install.ps1 may print
// banner / info lines first depending on Console.OutputEncoding effects).
// Find the last line that parses as JSON with a `stages` field.
const lines = result.stdout.split(/\r?\n/).filter(Boolean)
for (let i = lines.length - 1; i >= 0; i--) {
try {
const parsed = JSON.parse(lines[i])
if (parsed && Array.isArray(parsed.stages)) {
return parsed
}
} catch {
void 0
}
}
throw new Error(
`${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest'} produced no parseable JSON payload\n${result.stdout}`
)
}
// Parse the JSON result frame from a stage run. The protocol guarantees
// exactly one JSON line per stage in -Json or -Stage mode (post #27224 fix
// for the double-emit bug we addressed in the install.ps1 PR).
function parseStageResult(stdout) {
const lines = stdout.split(/\r?\n/).filter(Boolean)
for (let i = lines.length - 1; i >= 0; i--) {
try {
const parsed = JSON.parse(lines[i])
if (parsed && typeof parsed.ok === 'boolean' && typeof parsed.stage === 'string') {
return parsed
}
} catch {View on GitHub (pinned to c896c09c42)
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 '.'.
Example fix
# before
create_job(prompt="run tests", schedule="1h", workdir="myproject")
# after
from pathlib import Path
create_job(prompt="run tests", schedule="1h", workdir=str(Path("myproject").resolve())) Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def safe_workdir(w) -> str | None:
if w is None or not str(w).strip():
return None
p = Path(str(w).strip()).expanduser()
return str(p) if p.is_absolute() else str(p.resolve()) # make absolute up front Try / catch
try:
create_job(prompt=p, schedule=s, workdir=w)
except ValueError as e:
if "must be an absolute path" in str(e):
create_job(prompt=p, schedule=s, workdir=str(Path(w).resolve())) # retry absolute
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Remote connection is not ready yet. Try again in a moment.
- Remote gateway URL is required.
- Preview mode — launching is disabled.
- no install root
- Remote gateway URL is not valid: ${error.message}
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/dd9411bd1900f995.
Report an issue: GitHub.