stablyai/orca · error · Error

${response.error.message}

Error message

${response.error.message}

What it means

Generic browser-command dispatcher in the mobile browser pane: sendBrowserRequest() forwards an arbitrary method+params to the host with a 15s default timeout and throws when the RPC returns ok=false. The thrown message is the host's raw error.message; the surrounding catch maps it through browserErrorMessage() and shouldSurfaceBrowserError() to decide UI surfacing, returning null on failure.

Source

Thrown at mobile/src/browser/MobileBrowserPane.tsx:576

      params: Record<string, unknown> = {},
      opts: { showBusy?: boolean; suppressError?: boolean; timeoutMs?: number } = {}
    ): Promise<unknown | null> => {
      const base = pageParams()
      if (!client || !base) {
        return null
      }
      if (opts.showBusy) {
        busyRef.current = true
        setBusy(true)
      }
      try {
        const response = await client.sendRequest(
          method,
          { ...base, ...params },
          { timeoutMs: opts.timeoutMs ?? 15_000 }
        )
        if (!response.ok) {
          throw new Error((response as RpcFailure).error.message)
        }
        setError(null)
        return (response as RpcSuccess).result
      } catch (err) {
        const message = browserErrorMessage(err, 'Browser command failed')
        if (!opts.suppressError && shouldSurfaceBrowserError(message)) {
          setError(message)
        }
        return null
      } finally {
        if (opts.showBusy) {
          busyRef.current = false
          setBusy(false)
        }
      }
    },
    [client, pageParams]
  )

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Pass a larger opts.timeoutMs for slow navigations (navigateToAddress already uses 30s).
  2. Set opts.suppressError=true for best-effort commands where failure should not set the error banner.
  3. Check the host browser backend is alive (restart the host if Chromium crashed).
  4. Validate the URL via normalizeBrowserUrl before calling goto.

Example fix

// before
if (!response.ok) {
  throw new Error((response as RpcFailure).error.message)
}

// after — keep the code for branching in the caller
if (!response.ok) {
  const e = (response as RpcFailure).error
  throw new Error(`${e.code ? '[' + e.code + '] ' : ''}${e.message}`)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate URL and session before dispatching
if (!client || !pageParams()) return null
if (method === 'browser.goto') {
  const url = normalizeBrowserUrl(params.url as string)
  if (!url) { setError('Enter a valid URL.'); return null }
}

Type guard

function isBrowserRpcSuccess(r: RpcSuccess | RpcFailure): r is RpcSuccess {
  return r.ok
}

Try / catch

try {
  const response = await client.sendRequest(method, { ...base, ...params }, { timeoutMs: opts.timeoutMs ?? 15_000 })
  if (!response.ok) throw new Error((response as RpcFailure).error.message)
  return (response as RpcSuccess).result
} catch (err) {
  const message = browserErrorMessage(err, 'Browser command failed')
  if (!opts.suppressError && shouldSurfaceBrowserError(message)) setError(message)
  return null
}

Prevention

When it happens

Trigger: Any browser.* RPC (browser.goto, browser.click, browser.scroll, etc.) returns ok=false — page not found, navigation timeout, browser backend (Chromium/puppeteer) crashed on the host, invalid params, or a timeout exceeding 15s/opts.timeoutMs.

Common situations: Navigating to an unreachable URL, a host browser backend that crashed or OOMed, slow page loads exceeding the 15s default, or a worktree/pageId mismatch after the host recycled the browser session.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/1efb238d552c8290. Report an issue: GitHub.