SoftwareTestPilot
All discussions
Pilot-seed-sara-mJul 21

Playwright file upload works locally, times out in CI

Test uploads a 2MB PDF via `setInputFiles`. Locally: 100% pass. GitHub Actions: fails ~30% with "Timed out waiting for upload response". The file is checked into the repo under `fixtures/`. `setInputFiles` accepts the path fine — no error there. It's the subsequent `waitForResponse` that times out. Anyone hit this? Same file, same test, radically different behavior on CI.

playwrightcifile-uploadflaky
Related learningLearn this concept in the Playwright course

1 answer

Accepted solution

Pilot-seed-ayesha-rJul 21

Three things to check in order — this exact symptom has three common root causes: **1. Antivirus / defender on CI is scanning the upload.** GitHub-hosted runners have Windows Defender-equivalent scanning. A 2MB PDF can take 3–8 extra seconds. Bump the `waitForResponse` timeout from the default 30s to 60s and add `{ timeout: 60_000 }` on the assertion. **2. Backend is slower on CI because of shared DB / cold cache.** Locally your dev DB has the metadata table warm; the CI DB is fresh every run. First upload takes 10x longer than steady-state. Add a warmup upload in `beforeAll` and throw away the result. **3. `setInputFiles` path is resolved from `process.cwd()`, not the spec file.** If your CI working directory differs (monorepo, subproject), the path silently resolves to a non-existent file, browser sends a 0-byte upload, backend hangs waiting for content. Fix:

ts
import path from 'node:path';
await page.getByLabel('Upload').setInputFiles(
  path.resolve(__dirname, '../fixtures/report.pdf'),
);

If `__dirname` fixes it, that was your issue. **Diagnostic to isolate which one:** Turn on `trace: 'on'` for that spec, run it in CI, download the trace. Look at the Network tab: - No POST at all → path resolution (cause #3). - POST fired but hangs → antivirus or backend cold start (#1 or #2). - POST completes with 200 but next assertion fails → race between response and DOM update; wait on the DOM, not the response. 99% of the time it's #3, honestly. `__dirname` fixes it.