chenglou/pretext · error · Error
Firefox BiDi returned no browsing context
Error message
Firefox BiDi returned no browsing context
What it means
Thrown during Firefox session initialization when browsingContext.getTree succeeded (no error field) but the returned contexts array is empty (contexts[0]?.context is undefined). Firefox launched with about:blank but reported zero usable browsing contexts, so there is nowhere to navigate. This is distinct from error 17 (which fires when getTree itself errors); here the call succeeded with empty content.
Source
Thrown at scripts/browser-automation.ts:392
try {
await waitForPort(bidiPort)
bidi = await connectFirefoxBidi(bidiPort)
const session = await bidi.send('session.new', { capabilities: { alwaysMatch: {} } })
if (session.error !== undefined) {
throw new Error(session.message ?? session.error)
}
const tree = await bidi.send('browsingContext.getTree', {})
if (tree.error !== undefined) {
throw new Error(tree.message ?? tree.error)
}
const contexts = (tree.result as { contexts: Array<{ context: string }> }).contexts
const context = contexts[0]?.context
if (context === undefined) {
throw new Error('Firefox BiDi returned no browsing context')
}
return {
bidi,
context,
firefoxProcess,
profileDir,
}
} catch (error) {
bidi?.close()
try {
firefoxProcess.kill('SIGTERM')
} catch {
// Best effort cleanup.
}
rmSync(profileDir, { recursive: true, force: true })
throw error
}View on GitHub (pinned to ac49b09b7d)
Solutions
- Re-run — if it is a startup race, a second attempt may catch the context after it appears.
- Run Firefox headless manually with the same flags and confirm a tab opens (check about:debugging or ps for content processes).
- Ensure no other Firefox instance is using the same profile dir (the script uses a fresh mkdtemp profile, but verify).
- On containers/sandboxes, allow content-process forking and sufficient shared memory.
Defensive patterns
Strategy: retry
Type guard
function hasBrowsingContext(tree: { result?: { contexts?: Array<{ context: string }> } }): boolean {
return (tree.result?.contexts?.length ?? 0) > 0
} Try / catch
// Retry getTree once after a short delay in case the context appears late.
let contexts = (tree.result as { contexts: Array<{ context: string }> }).contexts
if (contexts.length === 0) {
await sleep(500)
const retry = await bidi.send('browsingContext.getTree', {})
contexts = (retry.result as { contexts: Array<{ context: string }> }).contexts
}
if (contexts[0]?.context === undefined) throw new Error('Firefox BiDi returned no browsing context') Prevention
- Ensure headless Firefox can spawn content processes (check sandbox/container limits).
- Use a fresh profile each run to avoid profile-lock contention.
- Confirm memory and /dev/shm are sufficient in containers for a content process.
- Re-run once — startup races sometimes resolve on the second attempt.
When it happens
Trigger: Firefox opened in --headless with --new-instance but produced no browsing context — e.g. a content process failed to start, a sandbox policy blocked tab creation, or a profile lock prevented a real session. The BiDi handshake worked but the browser has no tab.
Common situations: Headless Firefox on a restricted/sandboxed environment where content processes cannot spawn; a profile that Firefox considers locked by another instance; a minimal/containerized environment lacking the resources to create a tab; Firefox startup race where the context appears slightly after the query.
Related errors
- ${tree.message ?? tree.error}
- ${session.message ?? session.error}
- Timed out waiting for local port ${port}
- ${navigate.message ?? navigate.error}
- Firefox is not currently supported for keep-all oracle check
AI-assisted analysis of chenglou/pretext@ac49b09b7d (2026-08-12).
Data as JSON: /api/errors/15d997647b100ef7.
Report an issue: GitHub.