NousResearch/hermes-agent · error · Error

SshConnection requires a host.

Error message

SshConnection requires a host.

What it means

Constructor guard on SshConnection: the config object must exist and have a truthy host before any other validation or connection setup runs. It is the earliest possible failure for a malformed connection config — it fires before validateSshTarget, validateKeyPath, or control-path setup.

Source

Thrown at apps/desktop/electron/ssh-connection.ts:475

class SshConnection {
  host: string
  user: string
  port: number
  keyPath: string
  controlPath: string
  _spawnFn: any
  _log: (msg: string) => void
  _connectTimeoutMs: number
  _execTimeoutMs: number
  _forwardTimeoutMs: number
  _opened: boolean
  _mux: boolean
  _tunnels: Map<string, any>

  constructor(cfg, opts: any = {}) {
    if (!cfg || !cfg.host) {
      throw new Error('SshConnection requires a host.')
    }

    const port = cfg.port ? Number(cfg.port) : 22
    validateSshTarget(cfg.host, cfg.user || '', port)

    if (cfg.keyPath) {
      validateKeyPath(cfg.keyPath)
    }

    this.host = cfg.host
    this.user = cfg.user || ''
    this.port = port
    this.keyPath = cfg.keyPath || ''
    // Windows OpenSSH has no ControlMaster (mux sockets were never implemented
    // on Win32) — fall back to one ssh invocation per operation and a
    // persistent `ssh -N -L` child per tunnel. Empty controlPath routes the
    // pure builders onto their no-mux form.
    this._mux = opts.mux ?? process.platform !== 'win32'

View on GitHub (pinned to c896c09c42)

Solutions

  1. Ensure the config passed to SshConnection is a fully populated object with a non-empty host string.
  2. Check the persistence layer: the field written on save must be the field read on load (host).
  3. Guard call sites: skip connection attempts when the stored backend lacks a host and route the user back to remote setup.

Example fix

// before
const conn = new SshConnection(savedRemote)

// after
if (!savedRemote?.host) {
  throw new TypeError('remote backend config is missing host; re-run remote setup')
}
const conn = new SshConnection(savedRemote)
Defensive patterns

Strategy: type-guard

Validate before calling

if (!cfg || typeof cfg !== 'object' || typeof cfg.host !== 'string' || !cfg.host) {
  throw new TypeError('SshConnection config must be an object with a non-empty host string')
}

Type guard

function isSshConnectionConfig(cfg: unknown): cfg is { host: string; user?: string; port?: number | string; keyPath?: string } {
  return Boolean(cfg && typeof cfg === 'object' && typeof (cfg as any).host === 'string' && (cfg as any).host.length > 0)
}

Try / catch

try {
  conn = new SshConnection(cfg)
} catch (e) {
  if (e instanceof Error && e.message === 'SshConnection requires a host.') {
    // config incomplete: route the user back to remote setup instead of crashing
    openRemoteSetupDialog()
    return null
  }
  throw e
}

Prevention

When it happens

Trigger: new SshConnection(null), new SshConnection({}), or a config whose host field is missing/empty — e.g. deserializing a remote-backend entry that was never fully populated.

Common situations: Loading a saved remote backend whose JSON is missing the host key; passing a partially-built config object; a default-config factory returning {} before the user has filled anything in.

Related errors


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