Description
When a subframe's execution context is destroyed (e.g., iframe removed from DOM), foxbridge continues to route subsequent Runtime.evaluate calls to the destroyed context via latestCtx. This results in persistent "Failed to find execution context" errors that never recover — even after multiple retries with delays.
Environment
- foxbridge version: VulpineOS embedded (CDP server on :9222)
- Backend: juggler
- Camoufox: Firefox 146
- Platform: 7.0.9-1-cachyos Linux x86_64
Steps to Reproduce
- Connect to foxbridge CDP endpoint via WebSocket
- Navigate to a page that creates subframe iframes (e.g., Arkose Labs captcha enforcement iframe)
- Enable
Target.setAutoAttach with flatten:true
- Subframe loads →
Runtime.executionContextCreated fires with a new context (e.g., id:104, uniqueId:"id-5")
- Subframe is removed from DOM →
Runtime.executionContextDestroyed fires for the same context
- Send
Runtime.evaluate without contextId parameter (expecting foxbridge to use the default/main frame context)
- foxbridge returns:
"Failed to find execution context with id = id-5"
Expected Behavior
After Runtime.executionContextDestroyed fires for a context, foxbridge should either:
- Clear
latestCtx[session] for that session, OR
- Fall back to the main frame's default context when the stored
latestCtx points to a destroyed context
Actual Behavior
foxbridge routes Runtime.evaluate (with no contextId) to the destroyed context stored in latestCtx, producing a persistent error. Retries do not help because no new context is created to overwrite latestCtx.
Evidence
Log Timeline (from foxbridge CDP wire capture)
T+0ms — Subframe context created:
{
"method": "Runtime.executionContextCreated",
"params": {
"context": {
"auxData": {
"frameId": "subframe-12884901889",
"isDefault": true,
"type": "default"
},
"id": 104,
"name": "",
"origin": "",
"uniqueId": "id-5"
}
},
"sessionId": "005711cd-a7ec-49cf-8063-992307f5deb0"
}
T+0ms (same millisecond) — Subframe context destroyed:
{
"method": "Runtime.executionContextDestroyed",
"params": {
"executionContextId": 104,
"executionContextUniqueId": "id-5"
},
"sessionId": "005711cd-a7ec-49cf-8063-992307f5deb0"
}
T+2000ms onwards — Every Runtime.evaluate fails:
[runtime] evaluate error: error in channel "content::10/12/2":
exception while running method "evaluate" in namespace "page":
Failed to find execution context with id = id-5
findExecutionContext@chrome://juggler/content/content/Runtime.js:319:13
This error repeats every ~6 seconds for minutes — the stale context is never recovered.
Additional Evidence (from our application logs)
[22:43:07] Runtime.executionContextDestroyed: main frame context id=104 destroyed!
[22:43:09] Foxbridge Runtime.evaluate missing result object: CDP error -32000:
Failed to find execution context with id = id-5
[22:43:15] Foxbridge Runtime.evaluate missing result object: CDP error -32000:
Failed to find execution context with id = id-5
[22:43:21] Foxbridge Runtime.evaluate missing result object: CDP error -32000:
Failed to find execution context with id = id-5
... (continues every ~6 seconds, never recovers)
Analysis
Per the Context Management documentation:
When a Runtime.executionContextCreated event arrives, foxbridge updates latestCtx[jugglerSessionID] to the new context. All subsequent evaluate and callFunctionOn calls prefer this latest context over the caller's requested context ID.
The latestCtx map is updated on creation but not cleared on destruction. The documentation describes cleanup during navigation (executionContextsCleared), but not during subframe destruction (executionContextDestroyed).
The problematic sequence:
- Context created →
latestCtx[session] = "id-5"
- Context destroyed →
ctxMap entry removed, but latestCtx[session] still = "id-5"
Runtime.evaluate without contextId → foxbridge reads latestCtx[session] → tries to use "id-5" → Juggler throws
Suggested Fix
In the Runtime.executionContextDestroyed handler, when the destroyed context matches latestCtx[session], either:
Option A: Clear latestCtx[session] (set to empty/null), causing foxbridge to use Juggler's default context selection.
Option B: Fall back to the session's default/main frame context when latestCtx points to a destroyed context.
Option C: During evaluate with no explicit contextId, validate that latestCtx[session] still exists in ctxMap before using it. If not, fall back to default.
Workaround (client-side)
We currently work around this by:
- Explicitly passing the main frame's
contextId in Runtime.evaluate calls (bypasses latestCtx)
- Retry logic (3 attempts, 200ms delay) to catch the window between context destruction and
latestCtx update
Neither fix addresses the root cause in foxbridge.
Related
Description
When a subframe's execution context is destroyed (e.g., iframe removed from DOM), foxbridge continues to route subsequent
Runtime.evaluatecalls to the destroyed context vialatestCtx. This results in persistent"Failed to find execution context"errors that never recover — even after multiple retries with delays.Environment
Steps to Reproduce
Target.setAutoAttachwithflatten:trueRuntime.executionContextCreatedfires with a new context (e.g.,id:104, uniqueId:"id-5")Runtime.executionContextDestroyedfires for the same contextRuntime.evaluatewithoutcontextIdparameter (expecting foxbridge to use the default/main frame context)"Failed to find execution context with id = id-5"Expected Behavior
After
Runtime.executionContextDestroyedfires for a context, foxbridge should either:latestCtx[session]for that session, ORlatestCtxpoints to a destroyed contextActual Behavior
foxbridge routes
Runtime.evaluate(with nocontextId) to the destroyed context stored inlatestCtx, producing a persistent error. Retries do not help because no new context is created to overwritelatestCtx.Evidence
Log Timeline (from foxbridge CDP wire capture)
T+0ms — Subframe context created:
{ "method": "Runtime.executionContextCreated", "params": { "context": { "auxData": { "frameId": "subframe-12884901889", "isDefault": true, "type": "default" }, "id": 104, "name": "", "origin": "", "uniqueId": "id-5" } }, "sessionId": "005711cd-a7ec-49cf-8063-992307f5deb0" }T+0ms (same millisecond) — Subframe context destroyed:
{ "method": "Runtime.executionContextDestroyed", "params": { "executionContextId": 104, "executionContextUniqueId": "id-5" }, "sessionId": "005711cd-a7ec-49cf-8063-992307f5deb0" }T+2000ms onwards — Every
Runtime.evaluatefails:This error repeats every ~6 seconds for minutes — the stale context is never recovered.
Additional Evidence (from our application logs)
Analysis
Per the Context Management documentation:
latestCtx[session] = "id-5"ctxMapentry removed, butlatestCtx[session]still ="id-5"Runtime.evaluatewithoutcontextId→ foxbridge readslatestCtx[session]→ tries to use"id-5"→ Juggler throwsSuggested Fix
In the
Runtime.executionContextDestroyedhandler, when the destroyed context matcheslatestCtx[session], either:Option A: Clear
latestCtx[session](set to empty/null), causing foxbridge to use Juggler's default context selection.Option B: Fall back to the session's default/main frame context when
latestCtxpoints to a destroyed context.Option C: During
evaluatewith no explicitcontextId, validate thatlatestCtx[session]still exists inctxMapbefore using it. If not, fall back to default.Workaround (client-side)
We currently work around this by:
contextIdinRuntime.evaluatecalls (bypasseslatestCtx)latestCtxupdateNeither fix addresses the root cause in foxbridge.
Related