NousResearch/hermes-agent · error · Error
Unsafe SSH port: ${port} (must be 1-65535).
Error message
Unsafe SSH port: ${port} (must be 1-65535). What it means
Thrown by validateSshTarget() when the port does not coerce to an integer in 1-65535: the value is normalized with Number(port) then checked with Number.isInteger plus range bounds, so NaN, floats, zero, negatives, and out-of-range values all fail. Note the SshConnection constructor defaults cfg.port to 22 when falsy before validating, so this fires mainly when port is truthy-but-invalid or when validateSshTarget is called directly.
Source
Thrown at apps/desktop/electron/ssh-connection.ts:71
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}").`)
}
const p = Number(port)
if (!Number.isInteger(p) || p < 1 || p > 65535) {
throw new Error(`Unsafe SSH port: ${port} (must be 1-65535).`)
}
}
function validateKeyPath(keyPath) {
if (!keyPath) {
return
}
if (_CONTROL_CHAR_RE.test(keyPath)) {
throw new Error('Unsafe SSH key path: contains control characters.')
}
if (keyPath.startsWith('-')) {
throw new Error(`Unsafe SSH key path: must not start with a dash ("${keyPath}").`)
}
}
// Token / secret redactionView on GitHub (pinned to c896c09c42)
Solutions
- Set a valid integer port (1-65535), typically 22 for standard SSH.
- If the port is optional, leave it unset/undefined so the constructor's default of 22 applies, rather than storing a junk string.
- Coerce and validate at config save time: const p = Number(raw); if (!Number.isInteger(p) || p < 1 || p > 65535) reject.
Example fix
// before
new SshConnection({ host, user, port: cfg.portString }) // 'abc' or '70000'
// after
const port = cfg.portString ? Number(cfg.portString) : 22
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new RangeError(`invalid SSH port: ${cfg.portString}`)
new SshConnection({ host, user, port }) Defensive patterns
Strategy: validation
Validate before calling
function normalizeSshPort(raw: unknown): number {
const p = raw == null || raw === '' ? 22 : Number(raw)
if (!Number.isInteger(p) || p < 1 || p > 65535) {
throw new RangeError(`invalid SSH port: ${String(raw)}`)
}
return p
}
const port = normalizeSshPort(cfg.port) Type guard
function isValidSshPort(p: unknown): boolean {
const n = Number(p)
return p == null || p === '' || (Number.isInteger(n) && n >= 1 && n <= 65535)
} Try / catch
try {
validateSshTarget(host, user, port)
} catch (e) {
if (e instanceof Error && e.message.includes('Unsafe SSH port')) {
port = 22 // fall back to the default port after notifying the user
validateSshTarget(host, user, port)
} else throw e
} Prevention
- Store the port as a number in config, not a string; default to 22 when unset.
- Validate the port input (integer 1-65535) in the remote settings form.
- Parse ports out of URLs with Number(new URL(u).port || 22).
When it happens
Trigger: A truthy port like 'ssh' (NaN), 22.5, 0 is impossible via constructor (falsy → 22) but 70000, '0x10g', or '22.5' pass the truthy test and fail Number.isInteger/range. Calling validateSshTarget directly with undefined also fails (Number(undefined) = NaN).
Common situations: Config storing port as a non-numeric or empty-but-truthy string (e.g. ' ' or '22,'); a port field parsed from a URL string without conversion; user entering a value like '65536' or 'abc' in the port field.
Related errors
- SSH host is required.
- SSH remote mode is selected but no host is configured.
- Remote path must not be empty.
- Unsafe SSH target: host is required.
- Unsafe SSH target: host must not start with a dash ("${host}
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/4d983160d80d5d49.
Report an issue: GitHub.