microsoft/playwright · error · Error

Cannot set input files to detached input element

Error message

Cannot set input files to detached input element

What it means

Thrown by CRPage.setInputFilePaths (crPage.ts:340) when handle.ownerFrame(progress) returns null — the input element no longer belongs to any frame, i.e. it has been detached from the DOM. Without an owning frame there is no frame session to send DOM.setFileInputFiles to.

Source

Thrown at packages/playwright-core/src/server/chromium/crPage.ts:340

    }).catch(() => {});
  }

  stopScreencast() {
    this._mainFrameSession._client._sendMayFail('Page.stopScreencast').catch(() => {});
  }

  rafCountForStablePosition(): number {
    return 1;
  }

  async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
    return this._sessionForHandle(handle)._getContentQuads(handle);
  }

  async setInputFilePaths(progress: Progress, handle: dom.ElementHandle<HTMLInputElement>, files: string[]): Promise<void> {
    const frame = await handle.ownerFrame(progress);
    if (!frame)
      throw new Error('Cannot set input files to detached input element');
    const parentSession = this._sessionForFrame(frame);
    await progress.race(parentSession._client.send('DOM.setFileInputFiles', {
      objectId: handle._objectId,
      files
    }));
  }

  async adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>> {
    return this._sessionForHandle(handle)._adoptElementHandle<T>(handle, to);
  }

  async inputActionEpilogue(): Promise<void> {
    await this._mainFrameSession._client.send('Page.enable').catch(e => {});
  }

  async resetForReuse(progress: Progress): Promise<void> {
    // See https://github.com/microsoft/playwright/issues/22432.
    await this.rawMouse.move(progress, -1, -1, 'none', new Set(), new Set(), true);

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Re-query the locator right before setInputFiles so it resolves to the current element.
  2. Use locator.waitFor({ state: 'visible' }) before the upload to ensure stability.
  3. Anchor the input with a stable data-testid and a strict locator so re-renders do not invalidate it.

Example fix

// before
const input = page.locator('input[type=file]');
await someAsyncWork();
await input.setInputFiles('/tmp/a.png');
// after
await page.locator('[data-testid=upload]').waitFor({ state: 'visible' });
await page.locator('[data-testid=upload]').setInputFiles('/tmp/a.png');
Defensive patterns

Strategy: retry

Validate before calling

// Stabilize the input before uploading.
const upload = page.locator('[data-testid=upload]');
await upload.waitFor({ state: 'visible' });
await upload.setInputFiles('/tmp/a.png');

Try / catch

async function stableSetInputFiles(loc: Locator, files: string[]) {
  for (let i = 0; i < 3; i++) {
    try {
      await loc.setInputFiles(files);
      return;
    } catch (e) {
      if (!/detached input element/.test(String((e as Error).message)) || i === 2) throw e;
      await loc.waitFor({ state: 'visible' });
    }
  }
}

Prevention

When it happens

Trigger: Calling locator.setInputFiles() on an <input> that an SPA re-render removed; the input being detached between action resolution and the CDP call; replaced forms during async setup.

Common situations: React/Vue/Svelte re-rendering the input after state changes; file inputs inside components that remount on validation; uploads triggered after a navigation that swapped the form.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/4226fc471cb269ef. Report an issue: GitHub.