grafana/k6 · error

waitForEvent timed out after %v

Error message

waitForEvent timed out after %v

What it means

Thrown by BrowserContext.waitForEvent when no new page event satisfies the wait within the given timeout. The implementation selects on the context cancellation, a time.After(timeout) timer, the event channel, and the error channel; if the timer fires first you get 'waitForEvent timed out after <duration>'. Unlike the other branches, this is a plain timeout condition, not a wrapped lower-level failure.

Source

Thrown at internal/js/modules/k6/browser/common/browser_context.go:383

	}

	evCancelCtx, evCancelFn := context.WithCancel(b.ctx)
	defer evCancelFn() // This will remove the event handler once we return from here.

	chEvHandler := make(chan Event)
	ch := make(chan any)
	errCh := make(chan error)

	go b.runWaitForEventHandler(evCancelCtx, chEvHandler, predicateFn, ch, errCh)

	b.on(evCancelCtx, []string{EventBrowserContextPage}, chEvHandler)

	select {
	case <-b.ctx.Done():
		return nil, ContextErr(b.ctx) //nolint:wrapcheck
	case <-time.After(timeout):
		b.logger.Debugf("BrowserContext:WaitForEvent:timeout", "bctxid:%v event:%q", b.id, event)
		return nil, fmt.Errorf("waitForEvent timed out after %v", timeout)
	case evData := <-ch:
		b.logger.Debugf("BrowserContext:WaitForEvent:evData", "bctxid:%v event:%q", b.id, event)
		return evData, nil
	case err := <-errCh:
		b.logger.Debugf("BrowserContext:WaitForEvent:err", "bctxid:%v event:%q, err:%v", b.id, event, err)
		return nil, err
	}
}

// runWaitForEventHandler can work with a nil predicateFn. If predicateFn is
// nil it will return the response straight away.
func (b *BrowserContext) runWaitForEventHandler(
	ctx context.Context,
	chEvHandler chan Event, predicateFn func(p *Page) (bool, error),
	out chan<- any, errOut chan<- error,
) {
	b.logger.Debugf("BrowserContext:runWaitForEventHandler:go():starts", "bctxid:%v", b.id)
	defer b.logger.Debugf("BrowserContext:runWaitForEventHandler:go():returns", "bctxid:%v", b.id)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Perform the action that opens the new page after calling waitForEvent (waitForEvent resolves concurrently; the click/trigger must actually run), e.g. click the target=_blank link next.
  2. Pass a larger timeout in the options/predicate argument, e.g. waitForEvent('page', { timeout: 60_000 }).
  3. Debug why no page appears: check that popups are allowed, the selector clicked is correct, and look at debug logs for EventBrowserContextPage entries.
  4. If a predicate is used, verify it can return true (e.g. don't assert a URL the page never has).

Example fix

// before
const pagePromise = ctx.waitForEvent('page');
// nothing opens a new page -> timeout

// after
const pagePromise = ctx.waitForEvent('page', { timeout: 30000 });
await page.click('a[target="_blank"]'); // triggers the new tab
const newPage = await pagePromise;
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const newPage = await context.waitForEvent('page', { timeout: 30000 });
} catch (e) {
  if (e.message.includes('timed out')) {
    // no new page appeared: check the trigger action, popup settings, or raise the timeout
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling browserContext.waitForEvent('page', { timeout }) when no new page/tab is opened in the context during the timeout window, or when the predicate passed as the second argument never returns true for the pages that do open. Note waitForEvent does not itself open anything: your script must trigger the new page (e.g. click a link with target=_blank).

Common situations: Forgetting to perform the action that opens the new tab after registering the wait; a popup blocked by the browser (popup blocker, permission denied); the triggering click failing silently; timeout too short for a slow page under load; predicate conditions that never match.

Understand the failure class

Related errors


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