grafana/k6 · error
fail to fulfill request (id: %s): %w
Error message
fail to fulfill request (id: %s): %w
What it means
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.
Source
Thrown at internal/js/modules/k6/browser/common/network_manager.go:1013
if len(headers) > 0 {
action = action.WithResponseHeaders(headers)
}
if len(opts.Body) > 0 {
b64Body := base64.StdEncoding.EncodeToString(opts.Body)
action = action.WithBody(b64Body)
}
if err := action.Do(cdp.WithExecutor(m.ctx, m.session)); err != nil {
// Avoid logging as error when context is canceled.
// Most probably this happens when trying to fail a site's background request
// while the iteration is ending and therefore the browser context is being closed.
if errors.Is(err, context.Canceled) {
m.logger.Debug("NetworkManager:FulfillRequest", "context canceled fulfilling request")
return nil
}
return fmt.Errorf("fail to fulfill request (id: %s): %w",
request.interceptionID, err)
}
return nil
}
func toFetchHeaders(headers []HTTPHeader) []*fetch.HeaderEntry {
if len(headers) == 0 {
return nil
}
fetchHeaders := make([]*fetch.HeaderEntry, len(headers))
for i, header := range headers {
fetchHeaders[i] = &fetch.HeaderEntry{
Name: header.Name,
Value: header.Value,
}
}View on GitHub (pinned to 93accf6570)
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
Example fix
// before
page.route('**/login', async r => {
await sleep(2000);
await r.fulfill({ status: 200, body: 'ok' }); // page may have navigated on
});
// after
page.route('**/login', r => r.fulfill({ status: 200, body: 'ok' })); // immediate Defensive patterns
Strategy: try-catch
Validate before calling
if (page.isClosed()) { /* skip fulfilling */ } Try / catch
try {
await route.fulfill({ status: 200, body: 'ok' });
} catch (e) {
if (/fail to fulfill request/.test(String(e))) return; // stale interception
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- fail to abort request (id: %s): %w
- fail to continue request (id: %s): %w
- parent frame has been detached
- attaching new page: %w
- getting node in frame: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/0cb54d7d86364443.
Report an issue: GitHub.