{"record":{"id":"0cb54d7d86364443","repo":"grafana/k6","slug":"fail-to-fulfill-request-id-s-w","errorCode":null,"errorMessage":"fail to fulfill request (id: %s): %w","messagePattern":"fail to fulfill request \\(id: (.+?)\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/network_manager.go","lineNumber":1013,"sourceCode":"\tif len(headers) > 0 {\n\t\taction = action.WithResponseHeaders(headers)\n\t}\n\n\tif len(opts.Body) > 0 {\n\t\tb64Body := base64.StdEncoding.EncodeToString(opts.Body)\n\t\taction = action.WithBody(b64Body)\n\t}\n\n\tif err := action.Do(cdp.WithExecutor(m.ctx, m.session)); err != nil {\n\t\t// Avoid logging as error when context is canceled.\n\t\t// Most probably this happens when trying to fail a site's background request\n\t\t// while the iteration is ending and therefore the browser context is being closed.\n\t\tif errors.Is(err, context.Canceled) {\n\t\t\tm.logger.Debug(\"NetworkManager:FulfillRequest\", \"context canceled fulfilling request\")\n\t\t\treturn nil\n\t\t}\n\n\t\treturn fmt.Errorf(\"fail to fulfill request (id: %s): %w\",\n\t\t\trequest.interceptionID, err)\n\t}\n\n\treturn nil\n}\n\nfunc toFetchHeaders(headers []HTTPHeader) []*fetch.HeaderEntry {\n\tif len(headers) == 0 {\n\t\treturn nil\n\t}\n\n\tfetchHeaders := make([]*fetch.HeaderEntry, len(headers))\n\tfor i, header := range headers {\n\t\tfetchHeaders[i] = &fetch.HeaderEntry{\n\t\t\tName:  header.Name,\n\t\t\tValue: header.Value,\n\t\t}\n\t}","sourceCodeStart":995,"sourceCodeEnd":1031,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/network_manager.go#L995-L1031","documentation":"The CDP command Fetch.fulfillRequest failed while delivering a mocked response to an intercepted request. context.Canceled is already swallowed; any other CDP failure — stale interception ID, target destroyed mid-fulfill, malformed response rejected by chromium — is returned here.","triggerScenarios":"route.fulfill({status, body, headers}) executed after navigation destroyed the request, after unroute() disabled Fetch, or fulfilling the same route twice. Also when the composed response is rejected by the browser (e.g. invalid header values or content that breaks CDP framing) at the protocol level.","commonSituations":"Mocking API responses in handlers that await before fulfilling; tests where the page navigates immediately after a click while the handler is still mocking; double handlers for the same URL pattern (two page.route calls both matching).","solutions":["Fulfill synchronously and exactly once per intercepted request","Ensure fulfill() happens while the interception is still valid: before navigation completes or unroute() is called","Keep header values ASCII-safe and the body a valid string/Buffer; avoid huge bodies that race the target lifetime","Scope route patterns narrowly so only one handler matches"],"exampleFix":"// before\npage.route('**/login', async r => {\n  await sleep(2000);\n  await r.fulfill({ status: 200, body: 'ok' }); // page may have navigated on\n});\n\n// after\npage.route('**/login', r => r.fulfill({ status: 200, body: 'ok' })); // immediate","handlingStrategy":"try-catch","validationCode":"if (page.isClosed()) { /* skip fulfilling */ }","typeGuard":null,"tryCatchPattern":"try {\n  await route.fulfill({ status: 200, body: 'ok' });\n} catch (e) {\n  if (/fail to fulfill request/.test(String(e))) return; // stale interception\n  throw e;\n}","preventionTips":["Fulfill synchronously and once per request; avoid two route() calls matching the same URL","Scope route patterns narrowly so exactly one handler owns each request","Complete fulfill before triggering navigation that can invalidate the interception"],"tags":["cdp","browser","route","fulfill","mocking","race-condition"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}