microsoft/playwright · critical · TargetClosedError
Request context disposed.
Error message
Request context disposed.
What it means
Thrown as a TargetClosedError when the request is about to be sent but the APIRequestContext has already been disposed (e.g. the owning BrowserContext was closed). It uses _closeReason if available, falling back to 'Request context disposed.'
Source
Thrown at packages/playwright-core/src/server/fetch.ts:338
await this._updateRequestCookieHeader(progress, url, options.headers);
const requestCookies = getHeader(options.headers, 'cookie')?.split(';').map(p => {
const indexOfEquals = p.indexOf('=');
const name = indexOfEquals !== -1 ? p.substring(0, indexOfEquals).trim() : p.trim();
const value = indexOfEquals !== -1 ? p.substring(indexOfEquals + 1).trim() : '';
return { name, value };
}) || [];
const requestEvent: APIRequestEvent = {
url,
method: options.method!,
headers: options.headers,
cookies: requestCookies,
postData
};
this.emit(APIRequestContext.Events.Request, requestEvent);
if (this._disposed)
throw new TargetClosedError(this._closeReason || 'Request context disposed.');
let destroyRequest: (() => void) | undefined;
progress.setAllowConcurrentOrNestedRaces(true);
const resultPromise = new Promise<SendRequestResult>((fulfill, reject) => {
const requestConstructor: ((url: URL, options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void) => http.ClientRequest)
= (url.protocol === 'https:' ? https : http).request;
// If we have a proxy agent already, do not override it.
const agent = options.agent || (url.protocol === 'https:' ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent);
const requestOptions = { ...options, agent };
const startAt = monotonicTime();
const startAtWallTime = Date.now();
let reusedSocketAt: number | undefined;
let dnsLookupAt: number | undefined;
let tcpConnectionAt: number | undefined;
let tlsHandshakeAt: number | undefined;
let requestFinishAt: number | undefined;
let serverIPAddress: string | undefined;View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Ensure the request/context lifetime spans every call: create per-test or scope explicitly.
- Remove fire-and-forget requests that are not awaited before context teardown.
- If you need a standalone context, use request.newContext() and dispose it last.
- Re-check that you are not closing the browser/context inside the same test before the request resolves.
Example fix
// before: request outlives context
const context = await browser.newContext();
const req = context.request;
await context.close();
await req.get(url); // throws
// after
const req = await request.newContext();
try { await req.get(url); } finally { await req.dispose(); } Defensive patterns
Strategy: try-catch
Validate before calling
// Track context lifecycle before issuing requests
if (context.isClosed?.() ?? false) throw new Error('context closed');
await context.request.get(url); Type guard
// Detect a closed APIRequestContext safely
function isDisposed(e: unknown) {
return e instanceof Error && /disposed|Target page|context has been closed/i.test(e.message);
} Try / catch
try {
await request.get(url);
} catch (e) {
if (isDisposed(e)) { /* recreate context */ }
else throw e;
} Prevention
- Always pair newContext/newRequest with a matching dispose.
- Await all in-flight requests before closing the context.
When it happens
Trigger: Calling request.fetch/get/post on an APIRequestContext after context.dispose(), browser.close(), or after the test/fixture has torn down the BrowserContext. Also when an async request races with afterEach cleanup.
Common situations: Using a captured request fixture across tests; awaiting a long request that outlives the context; calling request methods in a finally block after context.close() ran; shared Playwright instance reused after shutdown.
Related errors
- ${this._closeReason}
- The object has been collected to prevent unbounded heap grow
- Cannot find object to "${method}": ${guid}
- Unknown new child: ${params.guid}
- Cannot find parent object ${parentGuid} to create ${guid}
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/71c3d29c42f471bf.
Report an issue: GitHub.