microsoft/playwright · warning · Error

Frame does not yet have a main execution context

Error message

Frame does not yet have a main execution context

What it means

nonStallingRawEvaluateInExistingMainContext needs a main (page) execution context that already exists (non-blocking). If the frame has not yet received its main context - e.g. right after attachment or before first commit - it throws 'Frame does not yet have a main execution context'.

Solutions

  1. Use the stalling context() method instead, which awaits the context promise.
  2. Wait for the frame's load/DOMContentLoaded lifecycle before evaluating.
  3. Ensure the frame is the one you expect (not a transient ad/popup iframe).
  4. Retry after a short waitForFunction on the page-level context.

Example fix

// before: existing-context call on a fresh iframe
const result = await frame.nonStallingRawEvaluate(...); // throws
// after: await the context
await frame.context('main');
// or wait for load
await frame.waitForLoadState?.('domcontentloaded');
Defensive patterns

Strategy: validation

Validate before calling

// Wait for the main context before evaluating
await page.frameLocator(selector).locator('body').waitFor();
// then operate

Type guard

// Guard: does the frame expose a context yet?
function hasMainContext(frame: any): boolean {
  return !!frame._contextData?.get('main')?.context;
}

Try / catch

null

Prevention

When it happens

Trigger: Running an evaluation that uses the 'existing context' (non-stalling) path on a frame that has not yet had its main world context created: freshly attached iframes, about:blank before commit, or frames mid-navigation before the new document's context is ready.

Common situations: Acting on a just-added iframe before DOMContentLoaded; cross-frame helper calls during rapid navigations; evaluations triggered from page event handlers that fire before the context is attached; timing-sensitive tests on slow CI.

Related errors


AI-assisted analysis of microsoft/playwright@f1d33b5029 (2026-08-12). Data as JSON: /api/errors/d44fed39a500c733. Report an issue: GitHub.

Appendix: source

Thrown at packages/playwright-core/src/server/frames.ts:624

      throw new EvaluationStalledError('Open JavaScript dialog prevents evaluation');

    const promise = new ManualPromise<T>();
    this._raceAgainstEvaluationStallingEventsPromises.add(promise);
    try {
      return await Promise.race([
        cb(),
        promise
      ]);
    } finally {
      this._raceAgainstEvaluationStallingEventsPromises.delete(promise);
    }
  }

  nonStallingRawEvaluateInExistingMainContext(expression: string): Promise<any> {
    return this.raceAgainstEvaluationStallingEvents(() => {
      const context = this.existingContext('main');
      if (!context)
        throw new Error('Frame does not yet have a main execution context');
      return context.rawEvaluateJSON(expression);
    });
  }

  nonStallingEvaluateInExistingContext(expression: string, world: types.World): Promise<any> {
    return this.raceAgainstEvaluationStallingEvents(() => {
      const context = this._contextData.get(world)?.context;
      if (!context)
        throw new Error('Frame does not yet have the execution context');
      return context.evaluateExpression(expression, { isFunction: false });
    });
  }

  _recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle?: Frame) {
    let isNetworkIdle = this._firedNetworkIdleSelf;
    for (const child of this._childFrames) {
      child._recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle);
      // We require networkidle event to be fired in the whole frame subtree, and then consider it done.

View on GitHub (pinned to f1d33b5029)