{"record":{"id":"3922acf700af7e7f","repo":"karatelabs/karate","slug":"main-frame-context-not-ready-retry","errorCode":null,"errorMessage":"main-frame context not ready, retry {}/{}: {}","messagePattern":"main-frame context not ready, retry (.+?)/(.+?): (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java","lineNumber":1872,"sourceCode":"            // Use explicit context ID for reliable frame targeting (see getFrameContext notes)\n            Integer contextId = getFrameContext();\n            // We INTEND to run in the main frame but its execution context is not\n            // confirmed live (currentFrame == null && getFrameContext() == null). This\n            // is distinct from the OOPIF null (currentFrame != null) and the same-origin\n            // child-frame null, both of which legitimately fall through to the default\n            // context below.\n            boolean mainContextUnresolved = currentFrame == null && contextId == null;\n            if (mainContextUnresolved && attempt < maxRetries) {\n                // Do NOT silently fall back to CDP's ambient default context here. Right\n                // after a switchFrame(null), the ambient default can still resolve to the\n                // child frame we just left, so the script runs in the wrong world and\n                // returns a misleading null instead of the main-frame value - the source\n                // of a rare CI flake (frame.feature \"Multiple switches\" reading\n                // window.mainValue as null under a slow context swap). Self-heal instead:\n                // give the replacement executionContextCreated more time via the retry\n                // loop, which re-awaits the readiness future on the next getFrameContext().\n                // Logged at WARN so a recurrence is visible in CI for the next investigation.\n                logger.warn(\"main-frame context not ready, retry {}/{}: {}\", attempt + 1, maxRetries, truncate(expression, 100));\n                sleep(transientInterval);\n                continue;\n            }\n            if (contextId != null) {\n                message.param(\"contextId\", contextId);\n            } else if (mainContextUnresolved) {\n                // Retries exhausted and the main context never registered - e.g. a\n                // download / 204 / aborted navigation that clears the context without a\n                // replacement. Degrade to CDP's default context as a last resort rather\n                // than hard-failing, matching the documented graceful-degradation intent,\n                // but log loudly so a recurrence stands out in CI logs.\n                logger.warn(\"main-frame context never became ready after {} attempts, falling back to default context: {}\", maxRetries, truncate(expression, 100));\n            }\n\n            CdpResponse response;\n            try {\n                response = message.send();\n            } catch (DialogOpenedException e) {","sourceCodeStart":1854,"sourceCodeEnd":1890,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java#L1854-L1890","documentation":"When evaluating an expression, the driver resolves the main frame's execution context via getFrameContext(); a slow context swap can make it return null, which would surface as a misleading null result. Instead of failing, the driver retries, logging this warning with the retry counter and a truncated expression, sleeping a short interval between attempts so the replacement executionContextCreated event can arrive.","triggerScenarios":"script()/eval on the main frame where getFrameContext() returns null (context not yet registered) for attempts < maxRetries, e.g. right after a navigation swapped the main-frame execution context.","commonSituations":"'Multiple switches' style tests reading window state immediately after frame switches under slow CI, evaluations racing a navigation's context replacement, pooled drivers resetting about:blank.","solutions":["No action usually needed — the retry loop self-heals once the context registers.","If the fallback (error 848) follows, add a short retry/wait in the test before evaluating right after navigation or frame switch.","Increase the transient retry interval/max retries via driver config if CI is chronically slow.","Update Chrome; late executionContextCreated events under load are a known flake source."],"exampleFix":"// before\nstring val = driver.script(\"window.mainValue\"); // immediately after frame switch, context still swapping\n// after\nkarate.waitFor(200); // or retry: let the replacement execution context register before evaluating\nstring val = driver.script(\"window.mainValue\");","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Wait briefly after frame switches/navigation before evaluating JS","Increase driver retry interval/count for slow CI","Update Chrome to reduce late executionContextCreated events","Avoid evaluating on pooled drivers immediately after reset"],"tags":["cdp","execution-context","retry","ci-flake"],"backgroundTag":"js-execution-context-not-ready","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}