{"record":{"id":"5e0e3a0937ad0ac5","repo":"chenglou/pretext","slug":"navigate-message-navigate-error","errorCode":null,"errorMessage":"${navigate.message ?? navigate.error}","messagePattern":"\\$\\{navigate\\.message \\?\\? navigate\\.error\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/browser-automation.ts","lineNumber":565,"sourceCode":"\n  function ensureState(): Promise<FirefoxSessionState> {\n    if (closed) {\n      return Promise.reject(new Error('Firefox automation session already closed'))\n    }\n    statePromise ??= initializeFirefoxSession()\n    return statePromise\n  }\n\n  return {\n    async navigate(url) {\n      const state = await ensureState()\n      const navigate = await state.bidi.send('browsingContext.navigate', {\n        context: state.context,\n        url,\n        wait: 'none',\n      })\n      if (navigate.error !== undefined) {\n        throw new Error(navigate.message ?? navigate.error)\n      }\n    },\n    async readLocationUrl() {\n      try {\n        const state = await ensureState()\n        const evaluation = await state.bidi.send('script.evaluate', {\n          expression: 'location.href',\n          target: { context: state.context },\n          awaitPromise: true,\n          resultOwnership: 'none',\n        })\n        if (evaluation.error !== undefined) {\n          return ''\n        }\n        return getBidiStringValue(evaluation)\n      } catch {\n        return ''\n      }","sourceCodeStart":547,"sourceCodeEnd":583,"githubUrl":"https://github.com/chenglou/pretext/blob/ac49b09b7d83ede19581fa94a8b892b07d309baf/scripts/browser-automation.ts#L547-L583","documentation":"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.","triggerScenarios":"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\".","commonSituations":"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.","solutions":["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.","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).","Confirm the Firefox process backing state.firefoxProcess is still alive; if it exited, recreate the session via createBrowserSession('firefox').","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.","Check that state.context still references an open tab by issuing a harmless `script.evaluate` (readLocationUrl already swallows errors) before re-navigating."],"exampleFix":"// before\nconst navigate = await state.bidi.send('browsingContext.navigate', {\n  context: state.context,\n  url,\n  wait: 'none',\n})\nif (navigate.error !== undefined) {\n  throw new Error(navigate.message ?? navigate.error)\n}\n// after — keep both fields and add the target url for diagnostics\nif (navigate.error !== undefined) {\n  const detail = navigate.message ?? navigate.error\n  throw new Error(`Firefox BiDi navigate to ${url} failed: ${detail}`)\n}","handlingStrategy":"try-catch","validationCode":"// Validate URL shape before handing it to the Firefox session\nfunction isValidNavigationUrl(url: string): boolean {\n  try {\n    const parsed = new URL(url)\n    return parsed.protocol === 'http:' || parsed.protocol === 'https:' || parsed.protocol === 'about:'\n  } catch {\n    return false\n  }\n}\nif (!isValidNavigationUrl(url)) throw new Error(`Refusing to navigate Firefox to invalid URL: ${url}`)","typeGuard":"// Narrow a BiDi response into success/error channels\nfunction isBidiError(res: { error?: unknown; message?: unknown }): res is { error: string; message?: string } {\n  return typeof res.error === 'string'\n}","tryCatchPattern":"try {\n  await session.navigate(url)\n} catch (error) {\n  if (error instanceof Error && /BiDi|navigate/i.test(error.message)) {\n    // Likely stale context or rejected URL — recreate the Firefox session once, then rethrow if it still fails\n    session.close()\n    session = createBrowserSession('firefox')\n    await session.navigate(url)\n  } else {\n    throw error\n  }\n}","preventionTips":["URL-encode every query parameter when building corpus/accuracy URLs so BiDi never sees an invalid absolute URL.","Keep the Firefox process alive across navigations; if you spawn it per-run, recreate the session rather than reusing a stale context.","Treat any BiDi response with an `error` field as terminal for that navigation — do not silently swallow and continue, the next call will likely fail too."],"tags":["browser-automation","firefox","bidi","network","navigation"],"backgroundTag":null,"analyzedSha":"ac49b09b7d83ede19581fa94a8b892b07d309baf","analyzedAt":"2026-08-12T17:03:16.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}