NousResearch/hermes-agent · error

Unsupported Hermes backend URL protocol: ${protocol}

Error message

Unsupported Hermes backend URL protocol: ${protocol}

What it means

ValueError from _validate_execution_mode (cron/jobs.py:1563): no_agent=True was set but no script was supplied. A no_agent job's entire body is the script — with neither an agent turn nor a script there is literally nothing to execute. Shared by create_job and update_job, so later updates can't create an empty no_agent job either.

Source

Thrown at apps/desktop/electron/dashboard-token.ts:16

/**
 * Helpers for local dashboard session-token discovery.
 *
 * The desktop main process can pass HERMES_DASHBOARD_SESSION_TOKEN when it
 * spawns the local dashboard, but the dashboard is the source of truth for the
 * token it actually serves to the renderer. If those drift, HTTP readiness
 * probes still pass while /api/ws rejects the renderer's token.
 */

const DEFAULT_TOKEN_FETCH_TIMEOUT_MS = 3_000

async function fetchPublicText(url, options: any = {}) {
  const { protocol } = new URL(url)

  if (protocol !== 'http:' && protocol !== 'https:') {
    throw new Error(`Unsupported Hermes backend URL protocol: ${protocol}`)
  }

  const timeoutMs = options.timeoutMs ?? DEFAULT_TOKEN_FETCH_TIMEOUT_MS

  const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) }).catch(error => {
    if (error.name === 'TimeoutError') {
      throw new Error(`Timed out connecting to Hermes backend after ${timeoutMs}ms`)
    }

    throw error
  })

  const text = await res.text()

  if (!res.ok) {
    throw new Error(`${res.status}: ${text || res.statusText}`)
  }

View on GitHub (pinned to c896c09c42)

Solutions

  1. Supply a script when using no_agent: create_job(..., no_agent=True, script='./collect.sh').
  2. If you want the model to run, just omit no_agent (default False) and keep the prompt.
  3. When updating, include both {'no_agent': True, 'script': ...} in the same call.

Example fix

# before
create_job(prompt="gather data", schedule="1h", no_agent=True)

# after
create_job(prompt="gather data", schedule="1h", no_agent=True, script="python gather.py > out.json")
Defensive patterns

Strategy: validation

Validate before calling

def valid_no_agent(no_agent: bool, script) -> bool:
    return (not no_agent) or bool(script)  # no_agent demands a script body

Try / catch

try:
    create_job(..., no_agent=True)
except ValueError as e:
    if "requires a script" in str(e):
        create_job(..., no_agent=True, script="./run.sh")
        raise

Prevention

When it happens

Trigger: create_job(prompt=..., no_agent=True) with no script=; or update_job(id, {'no_agent': True}) on a job whose script is None/empty.

Common situations: Copy-pasting a job template and deleting the script line; assuming the prompt still runs without an agent (it doesn't); enabling no_agent as a 'cost saving' without providing the replacement workload.

Related errors


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