NousResearch/hermes-agent · error · Error
Unsafe SSH target: host is required.
Error message
Unsafe SSH target: host is required.
What it means
First check in validateSshTarget() in the desktop app's ssh-connection module: the SSH host must be a non-empty string. It guards SshConnection construction and target building before any value reaches an ssh command line.
Source
Thrown at apps/desktop/electron/ssh-connection.ts:49
import { spawn } from 'node:child_process'
import crypto from 'node:crypto'
import fs from 'node:fs'
import net from 'node:net'
import os from 'node:os'
import path from 'node:path'
const DEFAULT_CONNECT_TIMEOUT_MS = 15_000
const DEFAULT_EXEC_TIMEOUT_MS = 20_000
const DEFAULT_FORWARD_TIMEOUT_MS = 15_000
const CONTROL_PERSIST_SECONDS = 300
// eslint-disable-next-line no-control-regex -- deliberately reject control chars in ssh targets
const _CONTROL_CHAR_RE = /[\x00-\x1f\x7f]/
function validateSshTarget(host, user, port) {
if (!host || typeof host !== 'string') {
throw new Error('Unsafe SSH target: host is required.')
}
if (host.startsWith('-')) {
throw new Error(`Unsafe SSH target: host must not start with a dash ("${host}").`)
}
if (_CONTROL_CHAR_RE.test(host)) {
throw new Error('Unsafe SSH target: host contains control characters.')
}
if (user && _CONTROL_CHAR_RE.test(user)) {
throw new Error('Unsafe SSH target: user contains control characters.')
}
if (user && user.startsWith('-')) {
throw new Error(`Unsafe SSH target: user must not start with a dash ("${user}").`)
}
View on GitHub (pinned to c896c09c42)
Solutions
- Require and validate host in the remote-backend form before attempting to connect.
- Check the config source: confirm the field the code reads (host) matches the key the config writer persists.
- If host comes from a URL, parse it explicitly: new URL(url).hostname.
Example fix
// before
const conn = new SshConnection({ host: cfg.server, user: cfg.user })
// after
if (!cfg.server) throw new TypeError('remote config missing server field')
const conn = new SshConnection({ host: new URL(cfg.server).hostname, user: cfg.user }) Defensive patterns
Strategy: type-guard
Validate before calling
if (!remoteCfg || typeof remoteCfg.host !== 'string' || remoteCfg.host.length === 0) {
throw new TypeError('Remote backend config must define a non-empty host string')
} Type guard
function isSshConfigWithHost(cfg: unknown): cfg is { host: string; user?: string; port?: number | string } {
return Boolean(cfg && 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.') {
promptUserToCompleteRemoteConfig()
return
}
throw e
} Prevention
- Make host a required field in the remote backend form and validate before save.
- Keep the persisted field name aligned with the constructor's expected cfg.host.
- Parse hosts from URLs with new URL(...).hostname rather than string splitting.
When it happens
Trigger: Constructing SshConnection with cfg.host undefined/null/'' or a non-string, or calling validateSshTarget directly with such a value. Typically a remote-backend config deserialized without a host field, or a connection form submitted with the host blank.
Common situations: Remote backend config missing the host key after a partial save; UI allowing submit with an empty host; code passing a URL where a bare hostname is expected so the field lookup returns ''; a config factory returning {} before the user has filled anything in.
Related errors
- SSH host is required.
- Remote path must not be empty.
- Remote gateway session token is required.
- SSH remote mode is selected but no host is configured.
- Unsafe remote path: contains NUL or newline.
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/c91e433b9e725999.
Report an issue: GitHub.