vercel/next.js · error · Error
An instant() scope is already active. Nesting instant() call
Error message
An instant() scope is already active. Nesting instant() calls is not supported. Did you forget to await the previous instant() call?
What it means
Thrown by the `instant()` test helper in next-playwright when an instant() scope is already active on the same browser context. The lock is per-context (tracked via a WeakSet) because the instant-navigation cookie is shared per context. Nesting is unsupported because two scopes would compete for the same cookie/lock. The message hints the most common cause: forgetting to await the previous call.
Source
Thrown at packages/next-playwright/src/index.ts:77
* automatically. For a fresh page (before any navigation), pass
* `baseURL` so the cookie can be scoped to the correct domain:
*
* await instant(page, async () => {
* await page.goto(url)
* // ...
* }, { baseURL: 'http://localhost:3000' })
*
* When `@playwright/test` is installed, acquire/release actions appear
* as labeled steps in the Playwright UI.
*/
export async function instant<T>(
page: PlaywrightPage,
fn: () => Promise<T>,
options?: { baseURL?: string }
): Promise<T> {
const context = page.context()
if (contextsWithActiveScope.has(context)) {
throw new Error(
'An instant() scope is already active. Nesting instant() ' +
'calls is not supported. Did you forget to await the ' +
'previous instant() call?'
)
}
// Resolve the cookie's scope before touching any browser state, so misuse on
// a fresh page (no baseURL and no prior navigation) fails with the
// descriptive error from resolveURL rather than half-entering a scope.
const { hostname } = new URL(resolveURL(page, options))
contextsWithActiveScope.add(context)
try {
// A completed prior scope on this context can leave the cookie behind (its
// client-side release races an in-flight captured-cookie write from a
// locked MPA page load; see the note above). No scope is active for this
// context, so a present cookie is always stale here — clear it before
// acquiring so a completed prior scope never blocks this one.View on GitHub (pinned to 0ae8c72462)
Solutions
- Ensure every `instant()` call is fully awaited before starting the next one on the same context.
- Do not nest instant() calls — move the inner logic outside the scope or run it after the outer scope completes.
- If you genuinely need concurrent instant scopes, use separate browser contexts (separate pages from different contexts).
- Wrap instant() calls in try/finally so an early throw doesn't leave the scope active.
Example fix
// before — second call overlaps the first
await instant(page, async () => { /* ... */ }) // missing await was here
await instant(page, async () => { /* ... */ })
// after — every call awaited
await instant(page, async () => { /* ... */ })
await instant(page, async () => { /* ... */ }) Defensive patterns
Strategy: validation
Validate before calling
// The library tracks active scopes internally via WeakSet.
// Caller-side: ensure no overlapping instant() calls by serializing them.
const instantQueue: Array<() => Promise<void>> = []
let instantRunning = false
async function safeInstant(page, fn) {
// Serialize instant() calls on the same context
await new Promise<void>(resolve => {
instantQueue.push(async () => { resolve(); await fn() })
if (!instantRunning) drain()
})
}
async function drain() {
instantRunning = true
while (instantQueue.length) { await instantQueue.shift()!() }
instantRunning = false
} Try / catch
try {
await instant(page, async () => { /* test code */ })
} catch (e) {
if (e.message.includes('already active')) {
// A previous instant() didn't finish — await it first
await new Promise(r => setTimeout(r, 100))
}
throw e
} Prevention
- Always `await` every instant() call before starting the next one on the same page/context.
- Never call instant() inside another instant() callback.
- Use separate browser contexts if you need concurrent instant scopes.
- Wrap instant() in try/finally so exceptions don't leave a scope in a bad state.
When it happens
Trigger: Calling `instant(page, fn1)` and then calling `instant(page, fn2)` (or the same page's context) before fn1 has resolved — e.g., missing `await`, or calling instant() inside another instant() callback.
Common situations: Forgot to `await` the first `instant()` call; sequential instant() calls in a loop where one rejects silently; calling instant() inside another instant() callback body; using the same browser context for parallel instant() calls.
Related errors
- This is an internal example for testing the CLI.
- NextRouter was not mounted. https://nextjs.org/docs/messages
- Proxy request failed: ${resp.status}
- Proxy request aborted [${request.method} ${request.url}]
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/b2ac0f257ee4df5c.
Report an issue: GitHub.