chenglou/pretext · error · Error

${navigate.message ?? navigate.error}

Error message

${navigate.message ?? navigate.error}

What it means

Thrown by the Firefox session's navigate() after sending a `browsingContext.navigate` command over the WebDriver BiDi WebSocket. If the BiDi response object carries an `error` field, the host throws using `navigate.message ?? navigate.error` — preferring the human-readable message and falling back to the raw error code. This is the Firefox counterpart of an AppleScript navigation failure: the BiDi peer (Firefox) accepted the command but reported it failed.

Source

Thrown at scripts/browser-automation.ts:565

  function ensureState(): Promise<FirefoxSessionState> {
    if (closed) {
      return Promise.reject(new Error('Firefox automation session already closed'))
    }
    statePromise ??= initializeFirefoxSession()
    return statePromise
  }

  return {
    async navigate(url) {
      const state = await ensureState()
      const navigate = await state.bidi.send('browsingContext.navigate', {
        context: state.context,
        url,
        wait: 'none',
      })
      if (navigate.error !== undefined) {
        throw new Error(navigate.message ?? navigate.error)
      }
    },
    async readLocationUrl() {
      try {
        const state = await ensureState()
        const evaluation = await state.bidi.send('script.evaluate', {
          expression: 'location.href',
          target: { context: state.context },
          awaitPromise: true,
          resultOwnership: 'none',
        })
        if (evaluation.error !== undefined) {
          return ''
        }
        return getBidiStringValue(evaluation)
      } catch {
        return ''
      }

View on GitHub (pinned to ac49b09b7d)

Solutions

  1. Read the exact `navigate.message ?? navigate.error` text in the thrown error — BiDi error codes (e.g. "no such frame", "invalid argument") point directly at the cause.
  2. Validate the URL is absolute and well-formed before calling session.navigate (the URL builders in corpus-check/font-matrix encode pieces, but a bad upstream value can still slip through).
  3. Confirm the Firefox process backing state.firefoxProcess is still alive; if it exited, recreate the session via createBrowserSession('firefox').
  4. If `wait: 'none'` is the issue on a newer Firefox, retry the navigation once after a short delay — some BiDi builds reject a second navigate issued before the first acknowledges.
  5. Check that state.context still references an open tab by issuing a harmless `script.evaluate` (readLocationUrl already swallows errors) before re-navigating.

Example fix

// before
const navigate = await state.bidi.send('browsingContext.navigate', {
  context: state.context,
  url,
  wait: 'none',
})
if (navigate.error !== undefined) {
  throw new Error(navigate.message ?? navigate.error)
}
// after — keep both fields and add the target url for diagnostics
if (navigate.error !== undefined) {
  const detail = navigate.message ?? navigate.error
  throw new Error(`Firefox BiDi navigate to ${url} failed: ${detail}`)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate URL shape before handing it to the Firefox session
function isValidNavigationUrl(url: string): boolean {
  try {
    const parsed = new URL(url)
    return parsed.protocol === 'http:' || parsed.protocol === 'https:' || parsed.protocol === 'about:'
  } catch {
    return false
  }
}
if (!isValidNavigationUrl(url)) throw new Error(`Refusing to navigate Firefox to invalid URL: ${url}`)

Type guard

// Narrow a BiDi response into success/error channels
function isBidiError(res: { error?: unknown; message?: unknown }): res is { error: string; message?: string } {
  return typeof res.error === 'string'
}

Try / catch

try {
  await session.navigate(url)
} catch (error) {
  if (error instanceof Error && /BiDi|navigate/i.test(error.message)) {
    // Likely stale context or rejected URL — recreate the Firefox session once, then rethrow if it still fails
    session.close()
    session = createBrowserSession('firefox')
    await session.navigate(url)
  } else {
    throw error
  }
}

Prevention

When it happens

Trigger: state.bidi.send('browsingContext.navigate', { context, url, wait: 'none' }) resolves with an object whose `error` is defined. Concrete causes: url is not a valid absolute URL (BiDi rejects relative/empty); the browsingContext id in state.context was closed (tab quit, Firefox restarted); Firefox is shutting down or crashed mid-navigation; a navigation to an about: or chrome:// URL blocked by BiDi; the BiDi implementation returned a protocol-level error like "invalid argument".

Common situations: Firefox process was killed or crashed between session creation and navigate (state.context now stale); the corpus/accuracy URL builder produced a malformed string (unencoded query, missing requestId); Firefox version upgrade changed BiDi error envelopes so `message` is undefined and only `error` (a terse code) is shown; navigating while a previous navigation on the same context has not settled and the BiDi server rejects the overlap.

Related errors


AI-assisted analysis of chenglou/pretext@ac49b09b7d (2026-08-12). Data as JSON: /api/errors/5e0e3a0937ad0ac5. Report an issue: GitHub.