{"id":"dd742fa71633de0d","repo":"vitest-dev/vitest","slug":"vitest-the-provider-was-closed","errorCode":null,"errorMessage":"[vitest] The provider was closed.","messagePattern":"\\[vitest\\] The provider was closed\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/browser-playwright/src/playwright.ts","lineNumber":656,"sourceCode":"  }\n\n  async openPage(sessionId: string, url: string, options: { parallel: boolean }): Promise<void> {\n    debug?.('[%s][%s] creating the browser page for %s', sessionId, this.browserName, url)\n    const browserPage = await this.openBrowserPage(sessionId, options)\n    debug?.('[%s][%s] browser page is created, opening %s', sessionId, this.browserName, url)\n    await browserPage.goto(url, { timeout: 0 })\n    await this._throwIfClosing(browserPage)\n  }\n\n  private async _throwIfClosing(disposable?: { close: () => Promise<void> }) {\n    if (this.closing) {\n      debug?.('[%s] provider was closed, cannot perform the action on %s', this.browserName, String(disposable))\n      await disposable?.close()\n      this.pages.clear()\n      this.contexts.clear()\n      this.browser = null\n      this.browserPromise = null\n      throw new Error(`[vitest] The provider was closed.`)\n    }\n  }\n\n  async getCDPSession(sessionid: string): Promise<CDPSession> {\n    const page = this.getPage(sessionid)\n    const cdp = await page.context().newCDPSession(page)\n    return {\n      send: cdp.send.bind(cdp),\n      on: cdp.on.bind(cdp),\n      off: cdp.off.bind(cdp),\n      once: cdp.once.bind(cdp),\n    } as any // overloaded CDPSession type is too tricky in monorepo\n  }\n\n  async close(): Promise<void> {\n    process.off('SIGTERM', this.onSIGTERM)\n\n    debug?.('[%s] closing provider', this.browserName)","sourceCodeStart":638,"sourceCodeEnd":674,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/browser-playwright/src/playwright.ts#L638-L674","documentation":"Thrown by PlaywrightBrowserProvider._throwIfClosing when this.closing is true. _throwIfClosing guards createContext, openBrowserPage, openPage, and post-open continuation: once close() has begun, any further operation is rejected, the disposable is closed, and the pages/contexts maps plus the browser handle are cleared.","triggerScenarios":"Issuing any command (openPage, getCommandsContext-driven ops, getContext, getCDPSession) after provider.close() has started, or racing openPage against a concurrent close (e.g. process exit / SIGTERM during teardown).","commonSituations":"Tests that issue browser actions in afterEach while the provider is being torn down, parallel runs where one worker closes the shared context, signal handlers aborting a run, or awaiting page operations that resolve after Vitest initiated shutdown.","solutions":["Ensure all browser interactions complete before provider.close()/process exit; move late assertions out of global teardown.","Avoid sharing a provider instance across workers (use per-worker contexts).","Catch the error around optional post-close cleanup and treat it as a no-op if the run is already shutting down.","Gate commands on provider.isOpen() / a custom closing flag before dispatch."],"exampleFix":"// before\nconst page = provider.getPage(sessionId) // may throw if close() raced\nawait page.click('#btn')\n\n// after\nif (!provider.closing && provider.pages.has(sessionId)) {\n  const page = provider.getPage(sessionId)\n  await page.click('#btn')\n}","handlingStrategy":"validation","validationCode":"if (provider.closing) {\n  throw new Error('provider is closing; skipping browser op')\n}\nconst page = provider.getPage(sessionId)","typeGuard":"function providerUsable(p: PlaywrightBrowserProvider): boolean {\n  return !p.closing && p.browser !== null\n}","tryCatchPattern":"try {\n  await provider.openPage(id, url, { parallel: false })\n} catch (err) {\n  if (err instanceof Error && /provider was closed/i.test(err.message)) {\n    // run is shutting down; skip optional post-close work\n  } else throw err\n}","preventionTips":["Finish all browser interactions before teardown/close.","Do not share one provider instance across workers.","Gate optional post-close cleanup on provider.closing."],"tags":["browser","playwright","lifecycle","teardown","race","vitest"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}