{"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":"exception","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/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/js/modules/k6/browser/browser/browser_context_mapping.go#L197-L226","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a real function: `waitForEvent('page', { predicate: (page) => page.url().includes('/home'), timeout: 10_000 })`","Or pass the predicate directly without the options wrapper: `waitForEvent('page', (page) => !!page)`","If the options come from JSON, keep only `timeout` there and attach the predicate in code: `const opts = { ...jsonOpts, predicate: myPredicate }`"],"exampleFix":"// before\nconst page = await browserContext.waitForEvent('page', {\n  predicate: 'page => page.url().includes(\"/order\")',\n});\n\n// after\nconst page = await browserContext.waitForEvent('page', {\n  predicate: (page) => page.url().includes('/order'),\n});","handlingStrategy":"type-guard","validationCode":"const opts = { timeout: 10_000, predicate: config.predicate }; // config may come from data\nif ('predicate' in opts && typeof opts.predicate !== 'function') {\n  throw new TypeError('waitForEvent: predicate must be a function, got ' + typeof opts.predicate);\n}\nconst page = await browserContext.waitForEvent('page', opts);","typeGuard":"const isPredicateFn = (v) => typeof v === 'function';\n// usage: isPredicateFn(opts.predicate) — also covers omitting it entirely (undefined is simply skipped)","tryCatchPattern":"try {\n  const p = await browserContext.waitForEvent('page', opts);\n} catch (e) {\n  if (String(e).includes('predicate function is not callable')) {\n    throw new Error('waitForEvent predicate was not a function — check how opts was built');\n  }\n  throw e;\n}","preventionTips":["Never store predicates in JSON/config files — functions do not survive serialization; attach them in code","Lint for quoted arrow functions in options objects (a common copy-paste slip)","Pass the predicate directly as the second argument when no timeout customization is needed","Keep Playwright docs open when porting examples: k6 mirrors the (event, optionsOrPredicate) signature"],"tags":["k6","browser","waitfor-event","predicate","playwright-api","type-mismatch"],"backgroundTag":"callback-not-a-function","analyzedSha":"01ffac6f245854c1b8adc6a69857c76a15f90022","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}