{"id":"e17be4af39f7bef8","repo":"vitest-dev/vitest","slug":"cannot-find-iframe-with-id-event-iframeid","errorCode":null,"errorMessage":"Cannot find iframe with id ${event.iframeId}","messagePattern":"Cannot find iframe with id (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/browser/src/client/orchestrator.ts","lineNumber":486,"sourceCode":"        }\n\n        await client.rpc.onUnhandledError(\n          {\n            name: 'Unexpected Event',\n            message: `Unexpected event: ${(e.data as any).event}`,\n          },\n          'Unexpected Event',\n        )\n      }\n    }\n  }\n\n  private iframeEvents = new WeakMap<HTMLIFrameElement, Set<string>>()\n\n  private async sendEventToIframe(event: IframeChannelOutgoingEvent): Promise<void> {\n    const iframe = this.iframes.get(event.iframeId)\n    if (!iframe) {\n      throw new Error(`Cannot find iframe with id ${event.iframeId}`)\n    }\n    let events = this.iframeEvents.get(iframe)\n    if (!events) {\n      events = new Set()\n      this.iframeEvents.set(iframe, events)\n    }\n    events.add(event.event)\n\n    const messageId = this.messageId++\n    channel.postMessage({ ...event, messageId } satisfies IframeReceivedEvent)\n\n    return new Promise<void>((resolve, reject) => {\n      let ackTimer: ReturnType<typeof setTimeout>\n\n      const cleanupEvents = () => {\n        clearTimeout(ackTimer)\n        channel.removeEventListener('message', onReceived)\n        this.eventTarget.removeEventListener('iframeerror', onError)","sourceCodeStart":468,"sourceCodeEnd":504,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/browser/src/client/orchestrator.ts#L468-L504","documentation":"Thrown by the browser orchestrator's sendEventToIframe when it cannot find an iframe registered under the given iframeId in its internal iframes Map. The orchestrator routes RPC events (viewport changes, screenshot commands, tester messages) to specific test iframes; this error means the routing target no longer exists. It almost always indicates the iframe was torn down (test finished, file reloaded, or runner restarted) while a queued event for it was still in flight.","triggerScenarios":"An IframeChannelOutgoingEvent is posted (e.g. via channel.postMessage in sendEventToIframe at packages/browser/src/client/orchestrator.ts:483) whose event.iframeId has already been removed from this.iframes (cleared at line 96 on destroy, or deleted at line 330 on iframe teardown). Happens during HMR reloads of a running browser test file, or when a test file finishes and a late viewport/screenshot RPC arrives for the removed iframe.","commonSituations":"Triggering HMR (editing a browser test file while it runs), closing the browser tab mid-run, Vitest server restart, or running with --browser where the iframe lifecycle races with queued screenshot/viewport commands. Also seen when an iframe id mismatch occurs after a reconnect.","solutions":["If seen during normal editing, re-run the test fresh: stop the run, then restart it so the orchestrator rebuilds its iframe registry.","Avoid triggering screenshots/viewport changes from outside the owning test's lifecycle (e.g. don't call page.screenshot in afterEach after the iframe may be gone).","Disable HMR for browser tests (vitest --no-watch or browser.uiProvider tweaks) if the race recurs frequently.","Upgrade @vitest/browser — late-event races against iframe teardown have been fixed across versions; ensure vitest and @vitest/browser match versions."],"exampleFix":"// before: screenshot fired after test body, races iframe teardown\nafterAll(() => page.screenshot())\n// after: capture inside the test where the iframe is guaranteed alive\nit('renders', async () => {\n  // ... assertions ...\n  await page.screenshot()\n})","handlingStrategy":"validation","validationCode":"// This is an internal orchestrator error; user code cannot fully prevent it.\n// Reduce the race by ensuring screenshot/viewport calls resolve inside the test:\nimport { page } from 'vitest/browser'\nconst current = (await import('vitest')).getWorkerState?.().current\nif (current && !current.concurrent) {\n  await page.screenshot()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never call page.screenshot/page.viewport from afterAll or module top-level — keep them inside the owning test body.","Disable HMR (--no-watch) for browser test runs where iframe lifecycle races appear.","Keep @vitest/browser and vitest on identical versions; the orchestrator/iframe contract is version-coupled."],"tags":["browser","orchestrator","iframe","lifecycle","internal"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}