{"record":{"id":"175966064f4f68ce","repo":"grafana/k6","slug":"page-route-s-iteration-ended-before-route-com","errorCode":null,"errorMessage":"page.route('%s'): iteration ended before route completed","messagePattern":"page\\.route\\('(.+?)'\\): iteration ended before route completed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/page_mapping.go","lineNumber":983,"sourceCode":"\t}\n\n\treturn a, o\n}\n\n// mapPageRoute maps the requested page.route event to the Sobek runtime.\nfunc mapPageRoute(vu moduleVU, p *common.Page) func(sobek.Value, sobek.Callable) (*sobek.Promise, error) {\n\treturn func(path sobek.Value, cb sobek.Callable) (*sobek.Promise, error) {\n\t\tctx := vu.Context()\n\n\t\tppath := parseStringOrRegex(path, false)\n\t\ttq := vu.get(ctx, p.TargetID())\n\n\t\troute := func(r *common.Route) error {\n\t\t\t_, err := queueTask(ctx, tq, func() (any, error) {\n\t\t\t\treturn cb(sobek.Undefined(), vu.Runtime().ToValue(mapRoute(vu, r)))\n\t\t\t})()\n\t\t\tif errors.Is(err, context.Canceled) {\n\t\t\t\treturn fmt.Errorf(\"page.route('%s'): iteration ended before route completed\", path)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"page.route('%s'): %w\", path, err)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\treturn promise(vu, func() (any, error) {\n\t\t\treturn nil, p.Route(ppath, route, newRegExMatcher(ctx, vu, tq))\n\t\t}), nil\n\t}\n}\n\nfunc mapWaitForURL(rt *sobek.Runtime, vu moduleVU, target interface {\n\tTimeout() time.Duration\n\tWaitForURL(urlPattern string, opts *common.FrameWaitForURLOptions, rm common.RegExMatcher) error\n}, url sobek.Value, opts sobek.Value,\n) (*sobek.Promise, error) {","sourceCodeStart":965,"sourceCodeEnd":1001,"githubUrl":"https://github.com/grafana/k6/blob/8d06114777191d21ef885d05c5f8aa9e1ed838ae/internal/js/modules/k6/browser/browser/page_mapping.go#L965-L1001","documentation":"Returned from the route callback registered by page.route(pattern, handler) when the k6 iteration (VU) context is canceled while the handler task is still queued or executing. queueTask returns context.Canceled, which is translated into this explicit message: the script iteration ended before the route handler completed.","triggerScenarios":"Registering page.route() and letting the iteration finish without the route ever being hit (or mid-handler); a route handler that awaits something long (e.g. route.fulfill delayed) while the scenario's iteration duration expires; graceful/normal scenario teardown with routes still active.","commonSituations":"Short per-iteration scenarios (e.g. 1s each) where the routed request never completes; forgetting page.unroute() at the end; route handlers performing sleeps or slow fixture building; browser close racing an in-flight intercepted request.","solutions":["Ensure every route handler synchronously settles the route (continue/fulfill/abort) without long awaits.","Call page.unroute(pattern) when done so no handler remains registered across iteration teardown.","Lengthen iteration/scene duration if the intercepted request legitimately needs more time.","Intercept fewer requests with a tighter pattern so handlers are not invoked near iteration end."],"exampleFix":"// before\nawait page.route('**/api/**', route => route.fulfill({ body: slowBody() })); // slow await may outlive iteration\n\n// after\nconst body = slowBody();\nawait page.route('**/api/**', route => route.fulfill({ body }));\n// ... at end of iteration:\nawait page.unroute('**/api/**');","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await page.route('**/api/**', handler);\n} catch (e) {\n  if (e.message.includes('iteration ended before route completed')) {\n    // benign at teardown: iteration duration expired before the intercepted request arrived\n    console.warn('route outlived iteration; consider unroute() earlier');\n  }\n}","preventionTips":["Always pair page.route() with page.unroute() at the end of the iteration.","Settle every intercepted request synchronously (continue/fulfill/abort) without long awaits.","Size iteration duration to comfortably cover the intercepted traffic."],"tags":["k6","browser","page","route","context-canceled","lifecycle"],"backgroundTag":"context-canceled","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"}