NousResearch/hermes-agent · warning · Error

Invalid preview URL

Error message

Invalid preview URL

What it means

The 'hermes:openPreviewInBrowser' IPC validates the URL via openPreviewInBrowser(url) (an async guard, typically restricting previews to localhost/loopback dev-server origins) before opening the system browser. A URL outside the allowed preview set, a malformed string, or a non-URL makes the guard resolve false and this error throws. Like openExternal, it is a guard, not a connectivity failure.

Source

Thrown at apps/desktop/electron/main.ts:11410

  // The match count arrives asynchronously via `found-in-page`; the
  // synchronous return value is intentionally `{ count: 0 }` to mirror
  // Electron's own `findInPage` return semantics (an opaque request id).
  return { count: 0 }
})

ipcMain.handle('hermes:stop-find-in-page', event => {
  const win = BrowserWindow.fromWebContents(event.sender)

  if (!win || win.isDestroyed()) {
    return
  }

  stopFind(win.webContents)
})

ipcMain.handle('hermes:openPreviewInBrowser', async (_event, url) => {
  if (!(await openPreviewInBrowser(url))) {
    throw new Error('Invalid preview URL')
  }
})

// User-configurable default project directory. The renderer reads this on
// settings mount and seeds the value into the picker; writing back persists
// it via writeDefaultProjectDir so resolveHermesCwd picks it up on the next
// session spawn (no app restart needed).
ipcMain.handle('hermes:setting:defaultProjectDir:get', async () => ({
  dir: readDefaultProjectDir(),
  defaultLabel: app.getPath('home'),
  resolvedCwd: resolveHermesCwd()
}))

ipcMain.handle('hermes:workspace:sanitize', async (_event, cwd) => sanitizeWorkspaceCwd(cwd))

ipcMain.handle('hermes:setting:defaultProjectDir:set', async (_event, dir) => {
  const next = typeof dir === 'string' && dir.trim() ? dir.trim() : null

View on GitHub (pinned to c896c09c42)

Solutions

  1. Print/inspect the url argument — confirm it is an absolute http(s) URL on an allowed (usually loopback) origin
  2. Wait for the preview/dev-server ready event that carries the final URL before enabling the open-in-browser button
  3. If a non-loopback preview origin is legitimate, extend openPreviewInBrowser's allow-list in the main process rather than bypassing it
  4. Disable the button when no preview URL is set instead of invoking the IPC with a stale value

Example fix

// before
onClick={() => void ipc.invoke('hermes:openPreviewInBrowser', previewUrl)}

// after
onClick={() => { if (!previewUrl) return; void ipc.invoke('hermes:openPreviewInBrowser', previewUrl) }}
Defensive patterns

Strategy: validation

Validate before calling

if (!previewUrl) { notify('No preview URL available yet'); return }
await ipc.invoke('hermes:openPreviewInBrowser', previewUrl)

Type guard

function isLoopbackHttpUrl(v: string): boolean { try { const u = new URL(v); return ['http:', 'https:'].includes(u.protocol) && ['localhost', '127.0.0.1', '[::1]'].includes(u.hostname) } catch { return false } }

Try / catch

catch (e) { if (e instanceof Error && e.message === 'Invalid preview URL') notify('Preview URL rejected — is the dev server running?') else throw e }

Prevention

When it happens

Trigger: Renderer requests opening a preview for a URL that is not an approved local preview origin (e.g. a remote https URL, a bare port '3000', undefined), or the preview server URL was constructed before the dev server reported its port.

Common situations: Clicking 'open in browser' for a preview pane whose dev server hasn't started or whose URL template produced null/undefined; passing a LAN address where the guard only allows 127.0.0.1/localhost.

Related errors


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