{"record":{"id":"cd2127ba7917131d","repo":"grafana/k6","slug":"waiting-for-page-s-event-w","errorCode":null,"errorMessage":"waiting for page %s event: %w","messagePattern":"waiting for page (.+?) event: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/page.go","lineNumber":1954,"sourceCode":"\n\t\tfor _, next := range handlers {\n\t\t\tif !yield(next.handler) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// waitForEvent subscribes to the given event and resolves once the predicate\n// determines an event should complete the wait or terminate with an error.\nfunc (p *Page) waitForEvent(\n\tctx context.Context,\n\teventName PageEventName,\n\tpredicate func(PageEvent) (bool, error),\n) (_ PageEvent, rerr error) {\n\tdefer func() {\n\t\tif rerr != nil {\n\t\t\trerr = fmt.Errorf(\"waiting for page %s event: %w\", eventName, rerr)\n\t\t}\n\t}()\n\n\ttype pageEventWaitResult struct {\n\t\tevent PageEvent\n\t\terr   error\n\t}\n\n\tvar (\n\t\tonce   sync.Once\n\t\tresult = make(chan pageEventWaitResult, 1)\n\t)\n\tid, err := p.addEventHandler(eventName, func(event PageEvent) error {\n\t\tok, perr := predicate(event)\n\t\tif perr == nil && !ok {\n\t\t\treturn nil\n\t\t}\n\t\t// We don't want to deadlock if another event frequently happens.","sourceCodeStart":1936,"sourceCodeEnd":1972,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/page.go#L1936-L1972","documentation":"Page.waitForEvent is the machinery behind page.waitForEvent and internal waits (popup, download, navigation); on any failure path — context canceled, wait timeout, predicate error, or channel error — a deferred wrapper adds 'waiting for page <name> event' around the cause. The error name in the message identifies which event the script was blocked on.","triggerScenarios":"page.waitForEvent('popup', {timeout: N}) where no popup opens before the timeout; the page or browser context closes while waiting; the predicate itself errors; the k6 iteration's context is canceled while blocked.","commonSituations":"Waiting for popups/downloads that never trigger (selector/flow assumptions wrong); timeout option too small under load; waiting on an event after page.close() was called elsewhere.","solutions":["Raise the timeout option passed to waitForEvent / waitForNavigation / waitForLoadState","Verify the user flow actually produces the event (manually trigger it once and confirm)","Ensure the page stays open for the whole wait — no concurrent close()","Cancel cleanly by scoping the wait with Promise.race or try/catch around teardown"],"exampleFix":"// before\nconst popup = await page.waitForEvent('popup'); // default timeout may be too short\n\n// after\nconst popup = await page.waitForEvent('popup', { timeout: 30000 });","handlingStrategy":"try-catch","validationCode":"null","typeGuard":null,"tryCatchPattern":"try {\n  const popup = await page.waitForEvent('popup', { timeout: 30000 });\n} catch (e) {\n  if (/waiting for page popup event/.test(e.message) && /timeout|timed out/i.test(e.message)) {\n    // flow never opened a popup: assert on the trigger, or raise timeout\n  } else throw e;\n}","preventionTips":["Always pass an explicit timeout sized to the real flow","Confirm the action that should emit the event is actually performed (click awaited, form submitted)","Keep the page open for the entire wait; close only after the wait resolves or rejects"],"tags":["browser","events","timeout","wait","page-lifecycle"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}