microsoft/playwright · error · NonRecoverableDOMError

Drop target did not accept the drop — its dragover handler d

Error message

Drop target did not accept the drop — its dragover handler did not call preventDefault()

What it means

HTML5 drag-and-drop requires the drop target's dragover handler to call event.preventDefault() to signal that it accepts the drop. The injected drop logic dispatches dragenter then dragover; if dragover's default is not prevented, it dispatches dragleave and returns 'not-accepted', and Playwright throws NonRecoverableDOMError. This mirrors real browser behavior: without preventDefault on dragover, the drop event never fires.

Source

Thrown at packages/playwright-core/src/server/dom.ts:718

            bubbles: true,
            cancelable: true,
            composed: true,
            clientX: point.x,
            clientY: point.y,
            dataTransfer: dt,
          });
          element.dispatchEvent(makeEvent('dragenter'));
          const over = makeEvent('dragover');
          element.dispatchEvent(over);
          if (!over.defaultPrevented) {
            element.dispatchEvent(makeEvent('dragleave'));
            return 'not-accepted' as const;
          }
          element.dispatchEvent(makeEvent('drop'));
          return 'accepted' as const;
        }, { payloads, data, point }));
        if (result === 'not-accepted')
          throw new NonRecoverableDOMError('Drop target did not accept the drop — its dragover handler did not call preventDefault()');
      } finally {
        if (disposeHandle)
          handle.dispose();
      }
    }, { ...options, waitAfter: 'disabled' });
  }

  async _setInputFiles(progress: Progress, items: InputFilesItems): Promise<'error:notconnected' | 'done'> {
    const { filePayloads, localPaths, localDirectory } = items;
    const multiple = filePayloads && filePayloads.length > 1 || localPaths && localPaths.length > 1;
    const result = await progress.race(this._evaluateHandleInUtility(([injected, node, { multiple, directoryUpload }]): Element | undefined => {
      const element = injected.retarget(node, 'follow-label');
      if (!element)
        return;
      if (element.tagName !== 'INPUT')
        throw injected.createStacklessError('Node is not an HTMLInputElement');
      const inputElement = element as HTMLInputElement;
      if (multiple && !inputElement.multiple && !inputElement.webkitdirectory)

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Target an element whose dragover handler calls preventDefault (a real drop zone).
  2. Ensure the app has registered the drop target before the drop call (e.g. enter a drag mode first).
  3. If the app uses a custom drag library, drive it via mouse moves/down/up instead of the HTML5 drop API.
  4. Verify the target with page.evaluate: check it has an active dragover→preventDefault listener.

Example fix

// before
await page.locator('#item').dragTo(page.locator('#not-a-dropzone')); // throws
// after
await page.locator('#item').dragTo(page.locator('#dropzone')); // dragover calls preventDefault
Defensive patterns

Strategy: validation

Validate before calling

// Verify the target is a real drop zone before dropping.
const accepts = await page.locator('#dropzone').evaluate(el => {
  let ok = false;
  el.addEventListener('dragover', e => { ok = true; }, { once: true });
  return ok;
});
await page.locator('#item').dragTo(page.locator('#dropzone'));

Prevention

When it happens

Trigger: Calling locator.drop() / elementHandle.drop() onto an element that is not a registered drop zone — its dragover handler does not call preventDefault (or it has no handler at all).

Common situations: Testing drag onto a plain <div> that the app only makes a drop target under certain conditions; targeting the wrong element; the app uses a custom (non-HTML5) drag library that ignores synthetic DnD events.

Related errors


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