microsoft/playwright · error · Error

Exactly one of payloads, localPaths and streams must be prov

Error message

Exactly one of payloads, localPaths and streams must be provided

What it means

prepareFilesForUpload requires exactly one source of input files. It counts the truthy values among payloads, localPaths, localDirectory, streams, and directoryStream and throws if the count is not exactly 1 - i.e. zero provided or more than one provided at once.

Source

Thrown at packages/playwright-core/src/server/fileUploadUtils.ts:43

import type * as types from './types';
import type * as channels from './channels';

// Keep in sync with the client.
export const fileUploadSizeLimit = 50 * 1024 * 1024;

async function filesExceedUploadLimit(files: string[]) {
  const sizes = await Promise.all(files.map(async file => (await fs.promises.stat(file)).size));
  return sizes.reduce((total, size) => total + size, 0) >= fileUploadSizeLimit;
}

export async function prepareFilesForUpload(frame: Frame, params: Omit<channels.ElementHandleSetInputFilesParams, 'timeout'>): Promise<InputFilesItems> {
  const { payloads, streams, directoryStream } = params;
  let { localPaths, localDirectory } = params;
  if (localPaths && !frame.attribution.playwright.options.isClientCollocatedWithServer)
    throw new Error('localPaths are not allowed when the client is not local');

  if ([payloads, localPaths, localDirectory, streams, directoryStream].filter(Boolean).length !== 1)
    throw new Error('Exactly one of payloads, localPaths and streams must be provided');

  if (streams)
    localPaths = streams.map(c => (c as WritableStreamDispatcher).path());
  if (directoryStream)
    localDirectory = (directoryStream as WritableStreamDispatcher).path();

  if (localPaths) {
    for (const p of localPaths)
      assert(path.isAbsolute(p) && path.resolve(p) === p, 'Paths provided to localPaths must be absolute and fully resolved.');
  }

  let fileBuffers: {
    name: string,
    mimeType?: string,
    buffer: Buffer,
    lastModifiedMs?: number,
  }[] | undefined = payloads;

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Pass exactly one source: a single paths array, OR a single payloads array, OR a single directory.
  2. Clear stale fields when refactoring from one input mode to another.
  3. If clearing files is the goal, use setInputFiles([]) explicitly via the dedicated path rather than ambiguous params.

Example fix

// before
await handle.setInputFiles(); // nothing -> throws
// after: pick exactly one
await handle.setInputFiles(['/abs/file.pdf']);
// or
await handle.setInputFiles([{ name: 'f.pdf', mimeType: 'application/pdf', buffer }]);
Defensive patterns

Strategy: validation

Validate before calling

// Enforce exactly-one before calling
function oneOf<T>(...vals: (T | undefined)[]): boolean {
  return vals.filter(v => v !== undefined && v !== null).length === 1;
}
if (!oneOf(paths, payloads)) throw new Error('specify exactly one source');

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling setInputFiles with no arguments, with multiple mutually-exclusive source types at once, or with an empty/undefined combination. Also reached internally if a dispatcher forwards two populated fields.

Common situations: Migrating between path-based and payload-based uploads and accidentally leaving both; calling setInputFiles() with an empty array; passing both streams and payloads in a custom integration.

Related errors


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