NousResearch/hermes-agent · error · Error

Remote path must be absolute or start with ~/: "${s}"

Error message

Remote path must be absolute or start with ~/: "${s}"

What it means

Thrown by validateRemotePath() when the path is non-empty and free of NUL/newlines but is neither absolute (leading /) nor home-relative (~ or ~/...). expandRemotePath() only supports those two forms because it rewrites them to '"$HOME"'-anchored or literal absolute strings inside a shell command; anything else is ambiguous on the remote host.

Source

Thrown at apps/desktop/electron/remote-lifecycle.ts:113

}

function validateRemotePath(p) {
  const s = String(p || '')

  if (!s) {
    throw new Error('Remote path must not be empty.')
  }

  // eslint-disable-next-line no-control-regex -- deliberately reject NUL in remote paths
  if (/[\x00\n\r]/.test(s)) {
    throw new Error('Unsafe remote path: contains NUL or newline.')
  }

  if (s === '~' || s.startsWith('~/') || s.startsWith('/')) {
    return
  }

  throw new Error(`Remote path must be absolute or start with ~/: "${s}"`)
}

function expandRemotePath(p) {
  validateRemotePath(p)

  if (p === '~') {
    return '"$HOME"'
  }

  if (p.startsWith('~/')) {
    return '"$HOME"' + shq(p.slice(1))
  }

  return shq(p)
}

// Resolve the remote hermes executable. An EXPLICIT path is honored strictly
// (throws a path-naming error if not executable — never silently falls back to a

View on GitHub (pinned to c896c09c42)

Solutions

  1. Use an absolute path (/opt/hermes) or a home-relative path (~/hermes) in the remote path setting.
  2. If the value is meant to be relative to the remote home, prefix it: `~/${value}`.
  3. Update the settings UI hint/validation to require the ~/ or / prefix at entry time.

Example fix

// before
expandRemotePath(userPath) // userPath = 'hermes'

// after
const p = userPath.startsWith('/') || userPath.startsWith('~') ? userPath : `~/${userPath}`
expandRemotePath(p)
Defensive patterns

Strategy: validation

Validate before calling

function isSupportedRemotePathForm(s: string): boolean {
  return s === '~' || s.startsWith('~/') || s.startsWith('/')
}

const p = String(config.path ?? '')
if (!isSupportedRemotePathForm(p)) {
  // normalize bare relative names to home-relative before validation
  config.path = `~/${p}`
}

Type guard

function isAbsoluteOrHomeRelative(p: unknown): p is string {
  return typeof p === 'string' && (p === '~' || p.startsWith('~/') || p.startsWith('/'))
}

Try / catch

try {
  return expandRemotePath(p)
} catch (e) {
  if (e instanceof Error && e.message.includes('must be absolute or start with ~/')) {
    return expandRemotePath(`~/${p}`) // retry with home-relative normalization
  }
  throw e
}

Prevention

When it happens

Trigger: Calling expandRemotePath('hermes'), expandRemotePath('./logs'), or expandRemotePath('x/y') — any bare or dot-relative path. The validator accepts only '~', '~/...', or '/...' and rejects everything else.

Common situations: Users entering a project-relative directory like 'hermes-agent' in a remote workdir setting; reusing a local relative cwd as a remote path; a form placeholder suggesting 'hermes' without the ~/ prefix.

Related errors


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