{"record":{"id":"641f7631059d6bd3","repo":"grafana/k6","slug":"getting-response-body-w","errorCode":null,"errorMessage":"getting response body: %w","messagePattern":"getting response body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/http.go","lineNumber":666,"sourceCode":"\t\tfor name, values := range r.extraHeaders {\n\t\t\theaders[strings.ToLower(name)] = strings.Join(values, \"\\n\")\n\t\t}\n\t} else {\n\t\tfor name, values := range r.headers {\n\t\t\theaders[strings.ToLower(name)] = strings.Join(values, \"\\n\")\n\t\t}\n\t}\n\tr.extraHeadersMu.RUnlock()\n\treturn headers\n}\n\n// Body returns the response body as a bytes buffer.\nfunc (r *Response) Body() ([]byte, error) {\n\tif r.status >= 300 && r.status <= 399 {\n\t\treturn nil, fmt.Errorf(\"response body is unavailable for redirect responses\")\n\t}\n\tif err := r.fetchBody(); err != nil {\n\t\treturn nil, fmt.Errorf(\"getting response body: %w\", err)\n\t}\n\n\tr.bodyMu.RLock()\n\tdefer r.bodyMu.RUnlock()\n\n\treturn r.body, nil\n}\n\n// bodySize returns the size in bytes of the response body.\nfunc (r *Response) bodySize() int64 {\n\t// Skip redirect responses\n\tif r.status >= 300 && r.status <= 399 {\n\t\treturn 0\n\t}\n\n\tif err := r.fetchBody(); err != nil {\n\t\tr.logger.Debugf(\"Response:bodySize:fetchBody\",\n\t\t\t\"url:%s method:%s err:%s\", r.url, r.request.method, err)","sourceCodeStart":648,"sourceCodeEnd":684,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/http.go#L648-L684","documentation":"Response.Body() first fetches the body over CDP (fetchBody, with its 5x retry loop for late-arriving data), then returns the cached bytes. This error wraps that fetch failure; the separate redirect-status guard is a distinct error, so hitting this one means the status was fine but Network.getResponseBody failed.","triggerScenarios":"Calling res.body() when the body cannot be retrieved: page navigated away or closed before the read, response fulfilled via route.fulfill() with no network data, cached/service-worker responses without retrievable bodies, or the DevTools session/browser gone.","commonSituations":"Deferring body reads until after navigation or teardown; intercepted requests; CI flakiness where the browser drops under load; asserting on large streaming responses.","solutions":["Call res.body() immediately after receiving the response object","Avoid reading bodies for intercepted (fulfilled) responses through this API","Ensure the page and context stay open until body assertions finish","If it happens only under load, investigate browser stability (memory, sandbox, zombie processes)"],"exampleFix":"// before\nconst res = await promiseOfNavigation; // stored for later\nawait doOtherWork();\nconst body = await res.body();\n\n// after\nconst res = await promiseOfNavigation;\nconst body = await res.body(); // read as soon as it arrives\nawait doOtherWork();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const body = await res.body();\n} catch (e) {\n  if (!/getting response body/.test(String(e.message))) throw e;\n  // body unavailable: page navigated/closed or fulfilled response\n}","preventionTips":["Call res.body() as soon as the response arrives","Do not hold Response objects across navigations for later body reads","Avoid body reads for intercepted requests"],"tags":["browser","network","response-body","cdp"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}