NousResearch/hermes-agent · error
Desktop installation ID is invalid.
Error message
Desktop installation ID is invalid.
What it means
ValueError from _validate in cron/notepad.py (the per-job key/value notepad store): job_id, after str() coercion, is empty ('' or resolves falsy). set_note/get_notes and sibling APIs require a job_id because every note is scoped to one cron job; an empty scope key would collide across jobs. Cheap, eager validation before any SQLite write.
Source
Thrown at apps/desktop/electron/desktop-installation.ts:127
} finally {
if (repairFd !== undefined) {
fs.closeSync(repairFd)
}
try {
fs.unlinkSync(repairPath)
} catch {
void 0
}
}
}
throw new Error('Could not repair the desktop installation ID.')
}
function sshOwnershipId(installationId, scope) {
if (!INSTALLATION_ID_RE.test(String(installationId || ''))) {
throw new Error('Desktop installation ID is invalid.')
}
return crypto
.createHash('sha256')
.update(`${installationId}\0${String(scope || '')}`)
.digest('hex')
.slice(0, 32)
}
export { INSTALLATION_ID_RE, loadOrCreateInstallationId, parseInstallationId, readInstallationId, sshOwnershipId }
View on GitHub (pinned to c896c09c42)
Solutions
- Pass a real job id — obtain one from create_job's return value or `hermes cron list`.
- Guard call sites: if not job_id: skip or raise before calling set_note.
- Note it must be non-empty AFTER str() coercion, so also avoid whitespace-only semantics confusion — only truly empty strings trip this.
Example fix
# before set_note(job_id="", key="status", value="ok") # after job = create_job(prompt=..., schedule="1h") set_note(job_id=job["id"], key="status", value="ok")
Defensive patterns
Strategy: validation
Validate before calling
def valid_note_ref(job_id, key) -> bool:
return bool(str(job_id)) and bool(key) Type guard
def is_non_empty_id(v) -> bool:
"""True when str(v) is a usable cron job reference."""
return isinstance(v, (str, int)) and bool(str(v).strip()) Try / catch
try:
set_note(job_id, key, value)
except ValueError as e:
if "job_id must be non-empty" in str(e):
# resolve a real id first: job = create_job(...) or lookup via list_jobs()
raise Prevention
- Capture the id from create_job's return dict and thread it through — never synthesize or default it.
- Guard tool-call boundaries where models may omit the job_id argument.
When it happens
Trigger: set_note('', 'status', 'ok'); passing a variable that was never assigned (empty string default); lookups where the job id came from a failed resolve_job_ref that returned None and was str()-coerced to 'None'... no — 'None' is truthy; the empty case is a literal '' / whitespace-less falsy value from missing input.
Common situations: Agent tool calls omitting the job_id argument; templates like f-string lookups where the id field is empty; code paths that create a note before the job id exists.
Related errors
- Unknown uninstall mode: ${mode}
- Preview mode — launching is disabled.
- no install root
- Remote gateway URL is not valid: ${error.message}
- ${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/fe1b795543f5faa7.
Report an issue: GitHub.