{"record":{"id":"eeb6e93b8485ec67","repo":"grafana/k6","slug":"the-listener-argument-must-be-a-function","errorCode":null,"errorMessage":"The \"listener\" argument must be a function","messagePattern":"The \"listener\" argument must be a function","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/page_mapping.go","lineNumber":776,"sourceCode":"\t\t\t}\n\t\t\tvar mehs []mapping\n\t\t\tfor _, eh := range ehs {\n\t\t\t\tehm := mapElementHandle(vu, eh)\n\t\t\t\tmehs = append(mehs, ehm)\n\t\t\t}\n\t\t\treturn mehs, nil\n\t\t})\n\t}\n\n\treturn maps\n}\n\n// mapPageOn enables using various page.on event handlers with the page.on method.\n// It provides a generic way to map different event types to their respective handler functions.\nfunc mapPageOn(vu moduleVU, p *common.Page) func(common.PageEventName, sobek.Callable) error {\n\treturn func(eventName common.PageEventName, handle sobek.Callable) error {\n\t\tif handle == nil {\n\t\t\tpanic(vu.Runtime().NewTypeError(`The \"listener\" argument must be a function`))\n\t\t}\n\n\t\tpageEvents := map[common.PageEventName]struct {\n\t\t\tmapp func(vu moduleVU, event common.PageEvent) mapping\n\t\t\twait bool // Whether to wait for the handler to complete.\n\t\t}{\n\t\t\tcommon.PageEventConsole:         {mapp: mapConsoleMessage},\n\t\t\tcommon.PageEventMetric:          {mapp: mapMetricEvent, wait: true},\n\t\t\tcommon.PageEventRequest:         {mapp: mapRequestEvent},\n\t\t\tcommon.PageEventResponse:        {mapp: mapResponseEvent},\n\t\t\tcommon.PageEventRequestFinished: {mapp: mapRequestEvent},\n\t\t\tcommon.PageEventRequestFailed:   {mapp: mapRequestEvent},\n\t\t}\n\t\tpageEvent, ok := pageEvents[eventName]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"unknown page on event: %q\", eventName)\n\t\t}\n","sourceCodeStart":758,"sourceCodeEnd":794,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/browser/page_mapping.go#L758-L794","documentation":"The browser module maps page.on(event, handler) onto typed internal handlers; before dispatching it checks that the second argument is a sobek.Callable. Passing anything that is not a function — undefined, null, an object, or the result of calling a function — raises this TypeError, mirroring Node's EventEmitter behavior.","triggerScenarios":"page.on('console', undefined) when a handler variable is missing; page.on('request', logRequest()) calling instead of referencing (returns undefined); page.on('response', { handle: fn }) passing an object with a method; forgetting the second argument entirely while testing. Applies to all page events: console, metric handled wait-style, request, response, requestFinished, requestFailed, etc.","commonSituations":"Optional handlers wired from config (handler only defined for some events); refactors renaming functions and leaving stale references that evaluate to undefined; copy-paste from browser docs into a k6 script where a helper wasn't ported; arrow-function typos like page.on('close', () => {}) written as page.on('close', () => {})) with a stray call.","solutions":["Pass the function reference, not its result: page.on('request', logRequest) not logRequest()","Guard optional handlers: if (handler) page.on('console', handler)","Default to a no-op: page.on('console', handler ?? (() => {}))","Check typeof handler === 'function' at the call site in shared test-framework code"],"exampleFix":"// before\npage.on('console', logger.log()); // invokes now, passes undefined\n\n// after\npage.on('console', logger.log); // reference only\n// optional handler:\npage.on('console', maybeHandler ?? (() => {}));","handlingStrategy":"type-guard","validationCode":"function onPage(page, event, handler) {\n  if (typeof handler !== 'function') throw new TypeError(`page.on('${event}') needs a function, got ${typeof handler}`);\n  return page.on(event, handler);\n}","typeGuard":"const isCallable = v => typeof v === 'function';\nconst safeHandler = (fn, fallback = () => {}) => isCallable(fn) ? fn : fallback;","tryCatchPattern":"try { page.on(event, handler); } catch (e) { if (/listener.*function/.test(e.message)) throw new TypeError(`handler for '${event}' missing or not a function`); throw e; }","preventionTips":["Pass function references, never call results: page.on('request', log) not log()","Default optional handlers: page.on('console', handler ?? (() => {}))","Type-check handlers in shared helpers before wiring events","Watch for undefined after renames — a missing import silently becomes undefined"],"tags":["browser","events","type-error","page-api"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}