microsoft/playwright · error · Error

At least one of "paths" or "data" must be provided.

Error message

At least one of "paths" or "data" must be provided.

What it means

Thrown by the browser_drop tool when params.paths is empty/undefined AND params.data is undefined. The tool's contract is that at least one payload source (filesystem paths or a MIME→value data map) must be supplied to construct the drag-drop event.

Source

Thrown at packages/playwright-core/src/tools/backend/files.ts:73

});

export const drop = defineTabTool({
  capability: 'core',

  schema: {
    name: 'browser_drop',
    title: 'Drop files or data onto an element',
    description: 'Drop files or MIME-typed data onto an element, as if dragged from outside the page. At least one of "paths" or "data" must be provided.',
    inputSchema: elementSchema.extend({
      paths: z.array(z.string()).optional().describe('Absolute paths to files to drop onto the element.'),
      data: z.record(z.string(), z.string()).optional().describe('Data to drop, as a map of MIME type to string value (e.g. {"text/plain": "hello", "text/uri-list": "https://example.com"}).'),
    }),
    type: 'action',
  },

  handle: async (tab, params, response) => {
    if (!params.paths?.length && !params.data)
      throw new Error('At least one of "paths" or "data" must be provided.');

    response.setIncludeSnapshot();
    const { locator, resolved } = await tab.targetLocator(params);

    if (params.paths)
      await Promise.all(params.paths.map(p => response.resolveClientFilename(p)));

    const payload: { files?: string | string[], data?: Record<string, string> } = {};
    if (params.paths?.length)
      payload.files = params.paths.length === 1 ? params.paths[0] : params.paths;
    if (params.data)
      payload.data = params.data;

    await tab.waitForCompletion(async () => {
      await locator.drop(payload, tab.actionTimeoutOptions);
    });

    response.addCode(`await page.${resolved}.drop(${formatObject(payload)});`);

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Provide at least one absolute path in params.paths.
  2. Or provide params.data as a Record<string,string> of MIME type to value (e.g. {"text/plain":"hello"}).
  3. Validate payload before the call: assert paths?.length || data.

Example fix

// before
await client.callTool('browser_drop', { element: 'dropzone', ref: 'e2' }); // throws

// after
await client.callTool('browser_drop', {
  element: 'dropzone', ref: 'e2',
  data: { 'text/plain': 'hello world' }
});
Defensive patterns

Strategy: validation

Validate before calling

function validateDropParams(p: { paths?: string[]; data?: Record<string,string> }) {
  if (!p.paths?.length && !p.data)
    throw new Error('browser_drop requires paths or data');
}
validateDropParams(params);
await client.callTool('browser_drop', params);

Type guard

function hasDropPayload(p: { paths?: unknown[]; data?: object }): boolean {
  return (!!p.paths && p.paths.length > 0) || (!!p.data && Object.keys(p.data).length > 0);
}

Try / catch

try {
  await client.callTool('browser_drop', params);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('At least one of "paths" or "data"')) {
    // supply a default data payload or ask the caller
  } else throw e;
}

Prevention

When it happens

Trigger: Invoking browser_drop with an empty paths array and no data, or omitting both fields entirely (both are .optional() in the schema, so zod does not catch the emptiness).

Common situations: Agent defaulting to {} for an optional-looking tool; building the payload conditionally and ending with neither branch populated.

Related errors


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