microsoft/playwright · error · Error
Response body is unavailable
Error message
Response body is unavailable
What it means
Thrown by createResponseBodyCallback (crNetworkManager.ts:595) when InterceptableRequest.from(request) returns undefined — the request has been detached from its CDP session (the symbol was cleared by detachIfNeeded). The response body can no longer be fetched via Network.getResponseBody because there is no session/route binding left.
Source
Thrown at packages/playwright-core/src/server/chromium/crNetworkManager.ts:595
readonly _originalRequestRoute: RouteImpl | undefined;
session: CRSession;
private static from(request: network.Request): InterceptableRequest | undefined {
return (request as any)[kInterceptableRequest];
}
static detachIfNeeded(request: network.Request, session: CRSession) {
if (InterceptableRequest.from(request)?.session === session)
(request as any)[kInterceptableRequest] = undefined;
}
static createResponseBodyCallback(request: network.Request): () => Promise<Buffer> {
return async () => {
// Lookup the request lazily, so that the response does not retain the
// InterceptableRequest and its session after detachIfNeeded().
const interceptable = InterceptableRequest.from(request);
if (!interceptable)
throw new Error('Response body is unavailable');
const contentLength = request._existingResponse()?.headerValue('content-length');
const expectedLength = contentLength ? +contentLength : undefined;
const session = interceptable.session;
const response = await session.send('Network.getResponseBody', { requestId: interceptable._requestId });
if (response.body || !expectedLength)
return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
// Make sure no network requests sent while reading the body for fulfilled requests.
if (interceptable._originalRequestRoute?._fulfilled)
return Buffer.from('');
// Re-fetching the resource may produce side effects on the server, only
// do it for GETs of static subresources and prefetch requests.
if (request.method() !== 'GET')
return Buffer.from('');
if (!kRefetchSafeResourceTypes.has(request.resourceType())) {
const rawHeaders = await request.internalRawRequestHeaders();View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Read response.body() before fulfilling or continuing the route.
- Cache the body bytes in the route handler if you need them later.
- Detach-aware: only read the body while the page that produced the response is still open.
Example fix
// before
route.fulfill({ status: 200 });
const body = await response.body();
// after
const body = await response.body();
await route.fulfill({ status: 200, body }); Defensive patterns
Strategy: validation
Validate before calling
// Read the body BEFORE fulfilling or continuing the route.
const response = await route.fetch();
const body = await response.body();
await route.fulfill({ status: response.status(), body, headers: response.headers() }); Prevention
- Treat route fulfillment as terminal: read everything you need first.
- Do not call response.body() after route.fulfill()/continue().
- Cache body bytes in the handler if multiple consumers need them.
When it happens
Trigger: Calling response.body() after route.fulfill() or route.continue() detached the request; reading the body after the page/context that owned the request was closed; accessing the body of a request whose route was already resolved.
Common situations: A request handler that fulfills a route and then logs response.body(); page.on('response') listeners that outlive the page; retries that re-read bodies after fulfill.
Related errors
- New URL must have same protocol as overridden URL
- Response body is unavailable for redirect responses
- Cannot fulfill with redirect status: ${response.status}
- PDF creation is only working with Chromium
- Timeout ${options.timeout}ms exceeded
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/a4687de1de0bda58.
Report an issue: GitHub.