microsoft/playwright · error · Error

File paths must be all files or a single directory

Error message

File paths must be all files or a single directory

What it means

Thrown by resolvePathsAndDirectoryForInputFiles after the loop, when at least one file path (localPaths) and one directory path (localDirectory) were collected together. setInputFiles requires the list to be all files OR exactly one directory — mixing is not allowed.

Source

Thrown at packages/playwright-core/src/client/elementHandle.ts:278

  return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= fileUploadSizeLimit;
}

async function resolvePathsAndDirectoryForInputFiles(items: string[]): Promise<[string[] | undefined, string | undefined]> {
  let localPaths: string[] | undefined;
  let localDirectory: string | undefined;
  for (const item of items) {
    const stat = await fs.promises.stat(item as string);
    if (stat.isDirectory()) {
      if (localDirectory)
        throw new Error('Multiple directories are not supported');
      localDirectory = path.resolve(item as string);
    } else {
      localPaths ??= [];
      localPaths.push(path.resolve(item as string));
    }
  }
  if (localPaths?.length && localDirectory)
    throw new Error('File paths must be all files or a single directory');
  return [localPaths, localDirectory];
}

export async function convertInputFiles(files: string | FilePayload | string[] | FilePayload[], context: BrowserContext): Promise<SetInputFilesFiles> {
  const items: (string | FilePayload)[] = Array.isArray(files) ? files.slice() : [files];

  if (items.some(item => typeof item === 'string')) {
    if (!items.every(item => typeof item === 'string'))
      throw new Error('File paths cannot be mixed with buffers');

    const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(items);

    if (context._connection.isRemote()) {
      const files = localDirectory ? (await fs.promises.readdir(localDirectory, { withFileTypes: true, recursive: true })).filter(f => f.isFile()).map(f => path.join(f.parentPath, f.name)) : localPaths!;
      const { writableStreams, rootDir } = await context._wrapApiCall(async () => context._channel.createTempFiles({
        rootDirName: localDirectory ? path.basename(localDirectory) : undefined,
        items: await Promise.all(files.map(async file => {
          const lastModifiedMs = (await fs.promises.stat(file)).mtimeMs;

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Split the call: upload files in one setInputFiles, the directory in another (or in a separate operation).
  2. Expand the directory to explicit file paths and pass only files.
  3. Pre-validate the list so it is either all-files or exactly-one-directory.

Example fix

// before
await locator.setInputFiles(['a.txt', './folder']);

// after — files only
const { readdirSync, statSync } = require('node:fs');
const { join } = require('node:path');
const expand = d => readdirSync(d).flatMap(f => { const p = join(d,f); return statSync(p).isDirectory() ? expand(p) : [p]; });
await locator.setInputFiles(['a.txt', ...expand('./folder')]);
Defensive patterns

Strategy: validation

Validate before calling

import { statSync } from 'node:fs';
function validateInputPaths(items: string[]) {
  let files = 0, dirs = 0;
  for (const p of items) { try { if (statSync(p).isDirectory()) dirs++; else files++; } catch {} }
  if (files > 0 && dirs > 0) throw new Error('Pass either all files or exactly one directory; mixing is not supported.');
  if (dirs > 1) throw new Error('At most one directory is supported.');
  return items;
}

Type guard

import { statSync } from 'node:fs';
type InputList = { kind: 'files'; paths: string[] } | { kind: 'directory'; path: string };
function classify(items: string[]): InputList {
  const files: string[] = [];
  const dirs: string[] = [];
  for (const p of items) { try { (statSync(p).isDirectory() ? dirs : files).push(p); } catch {} }
  if (dirs.length > 1) throw new Error('Multiple directories not supported');
  if (files.length && dirs.length) throw new Error('Files and a directory cannot be mixed');
  return dirs.length ? { kind: 'directory', path: dirs[0] } : { kind: 'files', paths: files };
}

Prevention

When it happens

Trigger: Passing an array that contains both regular files and a directory, e.g. setInputFiles(['a.txt', 'b.png', './folder']). The per-item directory check sets localDirectory, files populate localPaths, and the post-loop mix check fires.

Common situations: Glob results that include both files and a folder; user assembling uploads from disparate sources; expecting the directory to be uploaded alongside individual files.

Related errors


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