{"record":{"id":"1aa4936d2fcfba07","repo":"grafana/k6","slug":"predicate-must-be-a-function","errorCode":null,"errorMessage":"predicate must be a function","messagePattern":"predicate must be a function","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/page_mapping.go","lineNumber":1176,"sourceCode":"\tif k6common.IsNullish(opts) {\n\t\treturn ropts, nil, nil\n\t}\n\n\tif fn, ok := sobek.AssertFunction(opts); ok {\n\t\treturn ropts, fn, nil\n\t}\n\n\tobj := opts.ToObject(k6ext.Runtime(ctx))\n\n\tvar pred sobek.Callable\n\tfor _, k := range obj.Keys() {\n\t\tswitch k {\n\t\tcase \"timeout\":\n\t\t\tropts.Timeout = time.Duration(obj.Get(k).ToInteger()) * time.Millisecond\n\t\tcase \"predicate\":\n\t\t\tfn, ok := sobek.AssertFunction(obj.Get(k))\n\t\t\tif !ok {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"predicate must be a function\")\n\t\t\t}\n\t\t\tpred = fn\n\t\tdefault:\n\t\t\treturn nil, nil, fmt.Errorf(\"waitForEvent does not support option: '%s'\", k)\n\t\t}\n\t}\n\n\treturn ropts, pred, nil\n}\n\n// parseSize parses the size options from a Sobek value.\nfunc parseSize(rt *sobek.Runtime, opts sobek.Value) (*common.Size, error) {\n\tsize := &common.Size{}\n\tif k6common.IsNullish(opts) {\n\t\treturn size, nil\n\t}\n\n\tobj := opts.ToObject(rt)","sourceCodeStart":1158,"sourceCodeEnd":1194,"githubUrl":"https://github.com/grafana/k6/blob/8d06114777191d21ef885d05c5f8aa9e1ed838ae/internal/js/modules/k6/browser/browser/page_mapping.go#L1158-L1194","documentation":"Thrown by parsePageWaitForEventOptions when the 'predicate' option of page.waitForEvent is present but sobek.AssertFunction fails — the value is not callable. k6 requires an actual function reference; strings of source code, bound-object property names, or plain objects are rejected.","triggerScenarios":"page.waitForEvent('response', { predicate: 'r => r.ok()' }) (function as string); { predicate: { url: '/x' } } (object filter, unsupported in k6); passing result of calling the function instead of the function itself: { predicate: myPred() }.","commonSituations":"Translating Playwright's urlOrPredicate shorthand into a string; passing config-driven filter objects; accidentally invoking the function when passing it; JSON-serializing handlers from external config files.","solutions":["Pass a real function reference: { predicate: r => r.ok() }.","If building predicates from config data, construct a closure: const p = makePredicate(cfg); then { predicate: p }.","Do not call the function when passing it (no trailing parentheses).","Use an object-to-predicate adapter when config is data-only."],"exampleFix":"// before\nawait page.waitForEvent('response', { predicate: cfg.predicateExpr }); // string from config\n\n// after\nconst pred = new Function('r', 'return ' + cfg.predicateExpr);\nawait page.waitForEvent('response', { predicate: pred });","handlingStrategy":"type-guard","validationCode":"if (opts.predicate !== undefined && typeof opts.predicate !== 'function') {\n  throw new TypeError('opts.predicate must be a function');\n}\nawait page.waitForEvent('response', opts);","typeGuard":"const hasCallablePredicate = (o) => o?.predicate === undefined || typeof o.predicate === 'function';","tryCatchPattern":null,"preventionTips":["Pass function references, never strings of source code.","Do not invoke the predicate when passing it (no trailing parentheses).","When predicates come from external config, build closures with a factory function and typeof-check them."],"tags":["k6","browser","waitforevent","predicate","type-error"],"backgroundTag":"argument-type-mismatch","analyzedSha":"8d06114777191d21ef885d05c5f8aa9e1ed838ae","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}