{"record":{"id":"91615bebb56e5bc0","repo":"grafana/k6","slug":"browser-on-promise-rejected-w","errorCode":null,"errorMessage":"browser.on promise rejected: %w","messagePattern":"browser\\.on promise rejected: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/js/modules/k6/browser/common/browser.go","lineNumber":780,"sourceCode":"\tif err != nil {\n\t\treturn nil, spanRecordError(span, err)\n\t}\n\n\treturn page, nil\n}\n\n// On returns a Promise that is resolved when the browser process is disconnected.\n// The only accepted event value is \"disconnected\".\nfunc (b *Browser) On(event string) (bool, error) {\n\tif event != EventBrowserDisconnected {\n\t\treturn false, fmt.Errorf(\"unknown browser event: %q, must be %q\", event, EventBrowserDisconnected)\n\t}\n\n\tselect {\n\tcase <-b.browserProc.lostConnection:\n\t\treturn true, nil\n\tcase <-b.vuCtx.Done():\n\t\treturn false, fmt.Errorf(\"browser.on promise rejected: %w\", ContextErr(b.vuCtx))\n\t}\n}\n\n// UserAgent returns the controlled browser's user agent string.\nfunc (b *Browser) UserAgent() string {\n\treturn b.version.userAgent\n}\n\n// Version returns the controlled browser's version.\nfunc (b *Browser) Version() string {\n\tproduct := b.version.product\n\t_, after, ok := strings.Cut(product, \"/\")\n\tif !ok {\n\t\treturn product\n\t}\n\treturn after\n}\n","sourceCodeStart":762,"sourceCodeEnd":798,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/browser.go#L762-L798","documentation":"browser.on('disconnected') blocks until the browser loses its CDP connection; this error (browser.go:780) is returned when the VU context — the k6 iteration — is canceled first. ContextErr unwraps to context.Canceled or context.DeadlineExceeded, meaning the iteration or scenario ended (duration elapsed, timeout) while the promise was still pending, so the wait is aborted and the promise rejects.","triggerScenarios":"Awaiting browser.on('disconnected') without ever calling browser.close(); scenario duration or gracePeriod expiring while chromium is still connected; the browser being managed/closed outside this VU so disconnection never arrives within the iteration.","commonSituations":"Waiting for the browser to disconnect at the end of a test instead of initiating the disconnect; long-lived browser outliving the iteration; strict scenario timeouts in CI.","solutions":["Call browser.close() explicitly instead of passively waiting for disconnection","Wrap the await in Promise.race with your own timeout so the iteration ending never races the event","Do long waits in teardown(), where the browser lifecycle is still managed by k6, rather than at the tail of defaultFn"],"exampleFix":"// before\nawait browser.on('disconnected') // may reject: iteration ends first\n\n// after\nawait browser.close() // deterministic disconnect\n// or, if you must wait:\nawait Promise.race([browser.on('disconnected'), sleep(5000)])","handlingStrategy":"try-catch","validationCode":"// prefer deterministic disconnect:\n// await browser.close() instead of awaiting 'disconnected'","typeGuard":null,"tryCatchPattern":"try {\n  await browser.on('disconnected')\n} catch (e) {\n  if (/browser.on promise rejected/.test(String(e))) {\n    // iteration ended before disconnect — acceptable at test end\n  } else throw e\n}","preventionTips":["Call browser.close() rather than waiting for disconnection","Bound any wait with Promise.race and your own timeout","Move long lifecycle waits to teardown()"],"tags":["events","lifecycle","timeout","browser"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}