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

  1. Pass a real job id — obtain one from create_job's return value or `hermes cron list`.
  2. Guard call sites: if not job_id: skip or raise before calling set_note.
  3. 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

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


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/fe1b795543f5faa7. Report an issue: GitHub.