TurnSignal

npx playwright test --last-failed: re-run only the tests that failed

By · October 31, 2026 · 5 min read

In short

  • npx playwright test --last-failed runs only the tests that failed in the previous run. Playwright reads them from test-results/.last-run.json, which every run writes at the end.
  • In Playwright 1.63, if that file is missing (a fresh CI job, a cleaned workspace) --last-failed filters nothing and the whole suite runs. No warning is printed.
  • If the previous run had no failures, --last-failed selects no tests and the run stops with "No tests found". Add --pass-with-no-tests when that should count as success.
  • Tests that pass on the second try are flaky. A green re-run hides them unless you track them.

What --last-failed does

Run your suite, then run only what failed:

npx playwright test
npx playwright test --last-failed

Playwright's CLI describes the flag as "Only re-run the failures". It combines with other options: --last-failed --headed, --last-failed --debug and --last-failed --project=chromium all work. It is the quickest way to iterate on a few broken tests after a long run.

Where Playwright keeps the last run

At the end of every run (not with --list), Playwright writes .last-run.json into the output folder of the first selected project. With default settings that is test-results/.last-run.json. The file is small:

{
  "status": "failed",
  "failedTests": [
    "a1b2c3d4e5f6…-9f8e7d6c5b4a…"
  ]
}

The entries are test IDs. Playwright's reporter API says a test ID is computed from the test file name, the test title and the project name. Renaming a test or moving it to another file therefore changes its ID, and --last-failed will not find it.

Which tests count as failed

  • Tests whose outcome is not OK: failed, timed out, or passed when they were expected to fail.
  • Tests that never ran because the run stopped early, for example after maxFailures was reached. They are in the list too, so the next --last-failed run picks them up.
  • Not flaky tests. A test that failed and then passed on retry counts as OK, so it is not re-run.

Two options move the file: --last-failed-file <file> on the command line, or the PLAYWRIGHT_LAST_RUN_OUTPUT_FILE environment variable. The same setting controls where the next run writes it.

Why it runs everything in CI

In Playwright 1.63, --last-failed reads the file before the run starts. If the file does not exist or cannot be read, Playwright applies no filter at all, so every test runs, and nothing in the output says so. That is what happens in most CI setups:

  • "Re-run failed jobs" on GitHub Actions starts the job on a fresh runner. test-results/ from the failed attempt is gone.
  • A new pipeline in any CI system checks out a clean workspace.
  • A separate job that tries to re-run failures runs on a different machine than the job that failed.

So --last-failed in CI works in two ways only. Either in the same job, right after the full run, or by carrying the file over from the failed run as an artifact or cache and pointing --last-failed-file at it. The first is simpler:

# GitHub Actions step: full run, then one more pass over what failed
- name: Run Playwright tests
  run: npx playwright test || npx playwright test --last-failed

In a shell, a || b runs b only when a fails, and the step takes the exit code of the last command that ran. So the step passes when every failure passes on the second pass. Note what that means: you have just turned real failures into a green step, which is exactly what retries already does inside one run, with traces and the flaky label. Most teams are better served by retries: 2 in CI. Use the second pass only when a whole test run, not a single test, is what fails intermittently.

When the last run had no failures

If failedTests is empty, --last-failed keeps no tests. Playwright 1.63 then stops with "Error: No tests found", just as when a filter matches nothing. In the a || b pattern above this cannot happen, because b only runs after a failure. In other scripts, add --pass-with-no-tests so an empty selection is a success. The guide to "No tests found" lists the other causes of that error.

With shards

The failed-test filter is applied after Playwright has split the suite into shards. npx playwright test --shard=2/4 --last-failed keeps the shard's usual set of tests and then keeps only those that failed. Run it on the same shard (with the same shard's .last-run.json) for the result to make sense. A shard with no failures selects nothing, and with --shard set Playwright does not treat an empty selection as an error.

  • --only-changed [ref] runs test files affected by changes between HEAD and a git ref (all uncommitted changes by default). Playwright's CI docs suggest it as a quick first pass on pull requests, followed by the full suite, because it is a heuristic and can miss tests.
  • --repeat-each=N runs every selected test N times. With --last-failed --repeat-each=10 you can check whether a failure reproduces or was a one-off.
  • --fail-on-flaky-tests makes the run fail if any test was flaky, for teams that want retries to be information only.

Passing on the second try is still flaky

A test that fails in the full run and passes in the --last-failed pass did not get fixed; it is flaky. If the next run is green, nobody remembers that it failed. Over weeks, the number of tests that only pass on a second chance grows quietly. The guide to flaky Playwright tests covers finding the cause, and four questions help tell a flaky failure from a real one before you press re-run.

TurnSignal keeps every attempt of every test across runs, so a test that failed and then passed is recorded as flaky with the run and attempt behind it, and the next failure of that test on a pull request is labelled known flaky. Failures that are new on a branch are labelled new, so you know which ones to look at before re-running anything. See the docs.

Questions people ask

Does --last-failed work in the VS Code extension or UI mode?

The flag belongs to the npx playwright test command line and its .last-run.json file. UI mode and the VS Code extension keep their own test list, where you pick the tests to run.

Can I see which tests --last-failed will run before running them?

Yes: npx playwright test --last-failed --list prints the selected tests without running them. A --list run does not overwrite .last-run.json.

Why did --last-failed run my whole suite?

Because test-results/.last-run.json did not exist or could not be read, usually in a fresh CI job or after the folder was deleted. Playwright then applies no filter.

Is --last-failed the same as retries?

No. retries re-runs a failed test immediately within the same run and reports it as flaky if it then passes. --last-failed is a new run that only selects last time's failures, and it knows nothing about the earlier attempt.

Sources

Playwright details were checked against Playwright 1.63 and its documentation on October 31, 2026.

Try TurnSignal on your next run

Add one reporter line next to your existing reporters. Free to start, no credit card, no repository access.

Get started free Read the docs