vitest-dev/vitest · error · Error
[vitest] The provider was closed.
Error message
[vitest] The provider was closed.
What it means
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.
Source
Thrown at packages/browser-playwright/src/playwright.ts:656
}
async openPage(sessionId: string, url: string, options: { parallel: boolean }): Promise<void> {
debug?.('[%s][%s] creating the browser page for %s', sessionId, this.browserName, url)
const browserPage = await this.openBrowserPage(sessionId, options)
debug?.('[%s][%s] browser page is created, opening %s', sessionId, this.browserName, url)
await browserPage.goto(url, { timeout: 0 })
await this._throwIfClosing(browserPage)
}
private async _throwIfClosing(disposable?: { close: () => Promise<void> }) {
if (this.closing) {
debug?.('[%s] provider was closed, cannot perform the action on %s', this.browserName, String(disposable))
await disposable?.close()
this.pages.clear()
this.contexts.clear()
this.browser = null
this.browserPromise = null
throw new Error(`[vitest] The provider was closed.`)
}
}
async getCDPSession(sessionid: string): Promise<CDPSession> {
const page = this.getPage(sessionid)
const cdp = await page.context().newCDPSession(page)
return {
send: cdp.send.bind(cdp),
on: cdp.on.bind(cdp),
off: cdp.off.bind(cdp),
once: cdp.once.bind(cdp),
} as any // overloaded CDPSession type is too tricky in monorepo
}
async close(): Promise<void> {
process.off('SIGTERM', this.onSIGTERM)
debug?.('[%s] closing provider', this.browserName)View on GitHub (pinned to d568f8ce37)
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.
Example fix
// before
const page = provider.getPage(sessionId) // may throw if close() raced
await page.click('#btn')
// after
if (!provider.closing && provider.pages.has(sessionId)) {
const page = provider.getPage(sessionId)
await page.click('#btn')
} Defensive patterns
Strategy: validation
Validate before calling
if (provider.closing) {
throw new Error('provider is closing; skipping browser op')
}
const page = provider.getPage(sessionId) Type guard
function providerUsable(p: PlaywrightBrowserProvider): boolean {
return !p.closing && p.browser !== null
} Try / catch
try {
await provider.openPage(id, url, { parallel: false })
} catch (err) {
if (err instanceof Error && /provider was closed/i.test(err.message)) {
// run is shutting down; skip optional post-close work
} else throw err
} Prevention
- Finish all browser interactions before teardown/close.
- Do not share one provider instance across workers.
- Gate optional post-close cleanup on provider.closing.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Page "${sessionId}" not found in ${this.browserName} browser
- Cannot take a screenshot without a test path
- Element not found: ${v.element}
- The ${provider.name} provider does not support tracing.
- stopChunkTrace cannot be called outside of the test file.
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/dd742fa71633de0d.json.
Report an issue: GitHub.