grafana/k6 · error

predicate function failed: %w

Error message

predicate function failed: %w

What it means

Thrown inside BrowserContext.runWaitForEventHandler when the user-supplied predicate passed to waitForEvent returns an error. The predicate is invoked with each new *Page; if it errors, the error is wrapped as 'predicate function failed' and delivered through waitForEvent's error channel, so it surfaces as a rejection of the waitForEvent call.

Source

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

				continue
			}

			b.logger.Debugf("BrowserContext:runWaitForEventHandler:go():EventBrowserContextPage", "bctxid:%v", b.id)
			p, ok := ev.data.(*Page)
			if !ok {
				errOut <- fmt.Errorf("on create page event failed to return a page: %w", k6error.ErrFatal)
				return
			}

			if predicateFn == nil {
				b.logger.Debugf("BrowserContext:runWaitForEventHandler:go():EventBrowserContextPage:return", "bctxid:%v", b.id)
				out <- p
				return
			}

			retVal, err := predicateFn(p)
			if err != nil {
				errOut <- fmt.Errorf("predicate function failed: %w", err)
				return
			}

			if retVal {
				b.logger.Debugf(
					"BrowserContext:runWaitForEventHandler:go():EventBrowserContextPage:predicateFn:return",
					"bctxid:%v", b.id,
				)
				out <- p
				return
			}
		}
	}
}

func (b *BrowserContext) getSession(id target.SessionID) *Session {
	return b.browser.conn.getSession(id)
}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Inspect the wrapped error to find which operation inside the predicate failed and guard it.
  2. Make the predicate defensive: wrap page interactions in try/catch and return false on transient failures so the wait continues instead of failing.
  3. Only use cheap, synchronous checks in the predicate (match on URL/title) and do heavy work after waitForEvent resolves on the returned page.
  4. If the predicate must await something, return a promise that resolves to true/false rather than throwing.

Example fix

// before
const p = await ctx.waitForEvent('page', (page) => {
  return page.url().includes('/dashboard'); // may throw if not ready
});

// after
const p = await ctx.waitForEvent('page', (page) => {
  try {
    return page.url().includes('/dashboard');
  } catch {
    return false; // keep waiting instead of failing
  }
});
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const page = await context.waitForEvent('page', (p) => {
    try {
      return p.url().includes('/orders');
    } catch {
      return false; // transiently not ready: keep waiting
    }
  });
} catch (e) {
  if (e.message.includes('predicate function failed')) {
    // inspect the wrapped error: the predicate body itself threw
  }
}

Prevention

When it happens

Trigger: Calling browserContext.waitForEvent('page', (page) => { ... }) where the predicate body throws: calling a page API that fails (page.url(), page.title(), page.goto(...)), reading a property of an unexpectedly null object, or explicitly raising on a non-matching page.

Common situations: Predicates that immediately interact with the new page before it is ready (navigation not finished, frame not attached); predicates asserting on page.url() while the page is still on about:blank; predicates that call async APIs without awaiting them, producing unhandled rejections.

Related errors


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