grafana/k6 · error

waiting for function: execution context %q not found

Error message

waiting for function: execution context %q not found

What it means

Frame.waitForFunction() looks up the execution context for the requested world (main or utility) under a read lock; if it is nil you get 'waiting for function: execution context "<world>" not found'. waitForExecutionContext() only waits until f.ctx is done — so if the context never appears before the frame context is cancelled, this error fires. It means the frame has no JS world yet, or no longer has one.

Source

Thrown at internal/js/modules/k6/browser/common/frame.go:1987

}

func (f *Frame) waitForFunction(
	apiCtx context.Context, world executionWorld, js string,
	polling any, timeout time.Duration, args ...any,
) (any, error) {
	f.log.Debugf(
		"Frame:waitForFunction",
		"fid:%s furl:%q world:%s poll:%s timeout:%s",
		f.ID(), f.URL(), world, polling, timeout)

	f.waitForExecutionContext(world)

	f.executionContextMu.RLock()
	defer f.executionContextMu.RUnlock()

	execCtx := f.executionContexts[world]
	if execCtx == nil {
		return nil, fmt.Errorf("waiting for function: execution context %q not found", world)
	}
	injected, err := execCtx.getInjectedScript(apiCtx)
	if err != nil {
		return nil, fmt.Errorf("getting injected script: %w", err)
	}

	pageFn := `
		(injected, predicate, polling, timeout, ...args) => {
			return injected.waitForPredicateFunction(predicate, polling, timeout, ...args);
		}
	`

	// First evaluate the predicate function itself to get its handle.
	opts := evalOptions{forceCallable: false, returnByValue: false}
	handle, err := execCtx.eval(apiCtx, opts, js)
	if err != nil {
		return nil, fmt.Errorf("waiting for function, getting handle: %w", err)
	}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Ensure the frame is loaded before polling: await frame.waitForLoadState('domcontentloaded') then waitForFunction.
  2. If polling in iframes, re-fetch the frame by name/url from page.frames() instead of caching.
  3. Increase the k6 iteration timeout so the wait is not cut off.
  4. Check page.isClosed()/frame detachment and fail the iteration explicitly with a clearer message.

Example fix

// before
await frame.waitForFunction(() => window.ready === true);

// after
await frame.waitForLoadState('domcontentloaded');
await frame.waitForFunction(() => window.ready === true, { timeout: 30_000 });
Defensive patterns

Strategy: validation

Validate before calling

if (page.isClosed()) throw new Error('page closed');
await frame.waitForLoadState('domcontentloaded');
if (frame.isDetached?.()) throw new Error('frame detached before waitForFunction');

Try / catch

try { await frame.waitForFunction(fn, { timeout: 30_000 }); }
catch (e) { if (/execution context .* not found/.test(e.message)) { /* frame lifecycle: refetch frame, wait, retry once */ } throw e; }

Prevention

When it happens

Trigger: waitForFunction called on a frame that is detached, crashed, or whose renderer never created the utility world (very early navigation, about:blank); after page.close(); when the k6 iteration context is cancelled while waiting. Cross-origin iframes where world creation is delayed also hit this.

Common situations: Polling for a condition right after frame acquisition without waiting for load; iframes removed by the page mid-poll; k6 per-iteration timeout cancelling f.ctx while the poller waits.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/99169d65f566eda4. Report an issue: GitHub.