{"record":{"id":"4c144991293f95b1","repo":"grafana/k6","slug":"predicate-function-is-not-callable","errorCode":null,"errorMessage":"predicate function is not callable","messagePattern":"predicate function is not callable","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/browser_context_mapping.go","lineNumber":215,"sourceCode":"\t\tTimeout: defaultTime,\n\t}\n\n\tif k6common.IsNullish(optsOrPredicate) {\n\t\treturn w, nil\n\t}\n\tvar isCallable bool\n\tw.PredicateFn, isCallable = sobek.AssertFunction(optsOrPredicate)\n\tif isCallable {\n\t\treturn w, nil\n\t}\n\n\topts := optsOrPredicate.ToObject(rt)\n\tfor _, k := range opts.Keys() {\n\t\tswitch k {\n\t\tcase \"predicate\":\n\t\t\tw.PredicateFn, isCallable = sobek.AssertFunction(opts.Get(k))\n\t\t\tif !isCallable {\n\t\t\t\treturn nil, errors.New(\"predicate function is not callable\")\n\t\t\t}\n\t\tcase \"timeout\":\n\t\t\tw.Timeout = time.Duration(opts.Get(k).ToInteger()) * time.Millisecond\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown option: %s\", k)\n\t\t}\n\t}\n\n\treturn w, nil\n}\n","sourceCodeStart":197,"sourceCodeEnd":226,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/browser/browser_context_mapping.go#L197-L226","documentation":"browserContext.waitForEvent accepts either a bare predicate function or an options object; in the object form the `predicate` member must itself be callable. parseWaitForEventOptions uses sobek.AssertFunction, which fails for numbers, strings, undefined, or non-function values (browser_context_mapping.go:215).","triggerScenarios":"`context.waitForEvent('page', { predicate: fn() })` — invoking the function and passing its return value; `{ predicate: 'selector' }`; `{ predicate: undefined }`; also any unknown key in the object form yields the sibling 'unknown option' error.","commonSituations":"Copy-pasting Playwright examples where strings are accepted; accidentally executing the predicate while wiring it; refactoring the predicate into a value.","solutions":["Pass the function reference, not its result: `{ predicate: (page) => page.url().includes('x') }`","Or pass the predicate directly as the second arg: `waitForEvent('page', (page) => ...)`","Keep `timeout` (ms, number) as the only other key in the object form"],"exampleFix":"// before\nconst page = await context.waitForEvent('page', { predicate: isTargetPage() });\n\n// after\nconst page = await context.waitForEvent('page', { predicate: isTargetPage });","handlingStrategy":"type-guard","validationCode":"// Validate the options object before calling waitForEvent\nfunction isWaitForEventOpts(v) {\n  if (typeof v === 'function') return true;\n  if (v === null || typeof v !== 'object') return false;\n  const keys = Object.keys(v);\n  return keys.every((k) => k === 'predicate' || k === 'timeout') &&\n    (v.predicate === undefined || typeof v.predicate === 'function') &&\n    (v.timeout === undefined || typeof v.timeout === 'number');\n}","typeGuard":"const isPredicateCallable = (o) => o == null || typeof o === 'function' || typeof o.predicate === 'function';","tryCatchPattern":"try {\n  const page = await context.waitForEvent('page', { predicate: isTargetPage, timeout: 30_000 });\n} catch (e) {\n  if (/predicate function is not callable/.test(e.message)) throw new TypeError('waitForEvent: pass the function reference, not its return value');\n  throw e;\n}","preventionTips":["Pass function references (`isTargetPage`), never invocations (`isTargetPage()`)","Only `predicate` and `timeout` are accepted keys — selectors/strings are not supported","Model options on Playwright's waitForEvent but remember k6 requires a real callable predicate"],"tags":["browser","javascript","api-misuse","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}