{"record":{"id":"71c3d29c42f471bf","repo":"microsoft/playwright","slug":"request-context-disposed","errorCode":null,"errorMessage":"Request context disposed.","messagePattern":"Request context disposed\\.","errorType":"exception","errorClass":"TargetClosedError","httpStatus":null,"severity":"critical","filePath":"packages/playwright-core/src/server/fetch.ts","lineNumber":338,"sourceCode":"    await this._updateRequestCookieHeader(progress, url, options.headers);\n\n    const requestCookies = getHeader(options.headers, 'cookie')?.split(';').map(p => {\n      const indexOfEquals = p.indexOf('=');\n      const name = indexOfEquals !== -1 ? p.substring(0, indexOfEquals).trim() : p.trim();\n      const value = indexOfEquals !== -1 ? p.substring(indexOfEquals + 1).trim() : '';\n      return { name, value };\n    }) || [];\n    const requestEvent: APIRequestEvent = {\n      url,\n      method: options.method!,\n      headers: options.headers,\n      cookies: requestCookies,\n      postData\n    };\n    this.emit(APIRequestContext.Events.Request, requestEvent);\n\n    if (this._disposed)\n      throw new TargetClosedError(this._closeReason || 'Request context disposed.');\n\n    let destroyRequest: (() => void) | undefined;\n    progress.setAllowConcurrentOrNestedRaces(true);\n    const resultPromise = new Promise<SendRequestResult>((fulfill, reject) => {\n      const requestConstructor: ((url: URL, options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void) => http.ClientRequest)\n        = (url.protocol === 'https:' ? https : http).request;\n      // If we have a proxy agent already, do not override it.\n      const agent = options.agent || (url.protocol === 'https:' ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent);\n      const requestOptions = { ...options, agent };\n\n      const startAt = monotonicTime();\n      const startAtWallTime = Date.now();\n      let reusedSocketAt: number | undefined;\n      let dnsLookupAt: number | undefined;\n      let tcpConnectionAt: number | undefined;\n      let tlsHandshakeAt: number | undefined;\n      let requestFinishAt: number | undefined;\n      let serverIPAddress: string | undefined;","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/fetch.ts#L320-L356","documentation":"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.'","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: request outlives context\nconst context = await browser.newContext();\nconst req = context.request;\nawait context.close();\nawait req.get(url); // throws\n// after\nconst req = await request.newContext();\ntry { await req.get(url); } finally { await req.dispose(); }","handlingStrategy":"try-catch","validationCode":"// Track context lifecycle before issuing requests\nif (context.isClosed?.() ?? false) throw new Error('context closed');\nawait context.request.get(url);","typeGuard":"// Detect a closed APIRequestContext safely\nfunction isDisposed(e: unknown) {\n  return e instanceof Error && /disposed|Target page|context has been closed/i.test(e.message);\n}","tryCatchPattern":"try {\n  await request.get(url);\n} catch (e) {\n  if (isDisposed(e)) { /* recreate context */ }\n  else throw e;\n}","preventionTips":["Always pair newContext/newRequest with a matching dispose.","Await all in-flight requests before closing the context."],"tags":["lifecycle","api-request","context-disposed"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}