grafana/k6 · error

predicate function is not callable

Error message

predicate function is not callable

What it means

browserContext.waitForEvent(event, optsOrPredicate) accepts either a bare predicate function or an options object `{ predicate, timeout }`. parseWaitForEventOptions (internal/js/modules/k6/browser/browser/browser_context_mapping.go:193-225) first tries sobek.AssertFunction on the whole argument; if that fails it treats it as an options object and requires that its `predicate` key be callable. Passing a `predicate` value that is not a function (string, number, object, undefined assigned explicitly) throws this error, mirroring Playwright's API contract.

Solutions

  1. Pass a real function: `waitForEvent('page', { predicate: (page) => page.url().includes('/home'), timeout: 10_000 })`
  2. Or pass the predicate directly without the options wrapper: `waitForEvent('page', (page) => !!page)`
  3. If the options come from JSON, keep only `timeout` there and attach the predicate in code: `const opts = { ...jsonOpts, predicate: myPredicate }`

Example fix

// before
const page = await browserContext.waitForEvent('page', {
  predicate: 'page => page.url().includes("/order")',
});

// after
const page = await browserContext.waitForEvent('page', {
  predicate: (page) => page.url().includes('/order'),
});
Defensive patterns

Strategy: type-guard

Validate before calling

const opts = { timeout: 10_000, predicate: config.predicate }; // config may come from data
if ('predicate' in opts && typeof opts.predicate !== 'function') {
  throw new TypeError('waitForEvent: predicate must be a function, got ' + typeof opts.predicate);
}
const page = await browserContext.waitForEvent('page', opts);

Type guard

const isPredicateFn = (v) => typeof v === 'function';
// usage: isPredicateFn(opts.predicate) — also covers omitting it entirely (undefined is simply skipped)

Try / catch

try {
  const p = await browserContext.waitForEvent('page', opts);
} catch (e) {
  if (String(e).includes('predicate function is not callable')) {
    throw new Error('waitForEvent predicate was not a function — check how opts was built');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `browserContext.waitForEvent('page', { predicate: 'page => page.url().includes("/x")' })` (predicate as a string), `waitForEvent('page', { predicate: null })`, or building the options object from data/config where the function got lost (JSON has no functions). Passing a non-callable, non-object value does NOT hit this branch — only an object containing a non-callable `predicate` key does.

Common situations: Copy-pasting Playwright examples but quoting the arrow function; storing options in JSON/data files where functions cannot survive serialization; refactoring an inline predicate into a variable that ends up undefined; passing `{ timeout: 5000, predicate: someObj.method }` where method was unbound and undefined.

Related errors


AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18). Data as JSON: /api/errors/4c144991293f95b1. Report an issue: GitHub.

Appendix: source

Thrown at internal/js/modules/k6/browser/browser/browser_context_mapping.go:215

		Timeout: defaultTime,
	}

	if k6common.IsNullish(optsOrPredicate) {
		return w, nil
	}
	var isCallable bool
	w.PredicateFn, isCallable = sobek.AssertFunction(optsOrPredicate)
	if isCallable {
		return w, nil
	}

	opts := optsOrPredicate.ToObject(rt)
	for _, k := range opts.Keys() {
		switch k {
		case "predicate":
			w.PredicateFn, isCallable = sobek.AssertFunction(opts.Get(k))
			if !isCallable {
				return nil, errors.New("predicate function is not callable")
			}
		case "timeout":
			w.Timeout = time.Duration(opts.Get(k).ToInteger()) * time.Millisecond
		default:
			return nil, fmt.Errorf("unknown option: %s", k)
		}
	}

	return w, nil
}

View on GitHub (pinned to 01ffac6f24)