microsoft/playwright · error · Error
Frame has been detached.
Error message
Frame has been detached.
What it means
Thrown by CRPage._sessionForFrame (crPage.ts:149). It walks up the frame tree looking for a frame that owns a CDP frame session; if it reaches a frame with no parentFrame() before finding one, the frame has been detached from the document and no longer has a live target.
Source
Thrown at packages/playwright-core/src/server/chromium/crPage.ts:149
const frameSessions = Array.from(this._sessions.values());
await Promise.all(frameSessions.map(frameSession => {
if (frameSession._isMainFrame())
return cb(frameSession);
return cb(frameSession).catch(e => {
// Broadcasting a message to the closed iframe should be a noop.
if (isSessionClosedError(e))
return;
throw e;
});
}));
}
_sessionForFrame(frame: frames.Frame): FrameSession {
// Frame id equals target id.
while (!this._sessions.has(frame._id)) {
const parent = frame.parentFrame();
if (!parent)
throw new Error(`Frame has been detached.`);
frame = parent;
}
return this._sessions.get(frame._id)!;
}
private _sessionForHandle(handle: dom.ElementHandle): FrameSession {
const frame = handle._context.frame;
return this._sessionForFrame(frame);
}
willBeginDownload() {
this._mainFrameSession._willBeginDownload();
}
didClose() {
for (const session of this._sessions.values())
session.dispose();
this._page._didClose();View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Re-acquire the frame from page.frame() / page.frames() right before each action.
- Use frameLocator() with waitFor so a fresh frame is resolved each time.
- Guard with frame.isDetached() (or check parentFrame()) before acting.
Defensive patterns
Strategy: validation
Validate before calling
// Re-resolve the frame immediately before use; bail if detached.
const frame = page.frame({ name: 'widget' });
if (!frame || frame.isDetached()) throw new Error('widget frame is gone');
await frame.evaluate(() => document.title); Type guard
function isLiveFrame(f: Frame | null | undefined): f is Frame {
return !!f && !f.isDetached() && !!f.parentFrame();
} Prevention
- Never cache Frame references across await boundaries that may navigate.
- Use page.frameLocator() with built-in retry instead of imperative frame lookups.
- Check frame.isDetached() before each action.
When it happens
Trigger: Operating on a Frame object whose <iframe> was removed from the DOM (OOIF detached); calling frame.evaluate / frame.click after the iframe was torn down; holding a stale Frame reference from page.frames().
Common situations: SPAs that remove iframes during state changes; tests that cache childFrames() then act on them after a route change; popups closed mid-action.
Related errors
- Cannot set input files to detached input element
- ${name}: expected one of (load|domcontentloaded|networkidle|
- PDF creation is only working with Chromium
- The object has been collected to prevent unbounded heap grow
- Cannot find object to "${method}": ${guid}
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/e5c12e84a3b5fb18.
Report an issue: GitHub.