What happened?
Running --shard=N/M together with --parallel exits non-zero even though all tests pass — but only when a tests/.pest/shards.json timings file is absent or doesn't cover the sharded classes (i.e. the count-based fallback path). With a complete, fresh shards.json (time-balanced path), the same command exits 0.
Correction to the original report: I first attributed this to PHPUnit 12's failOnPhpunitWarning default flipping the exit code on a "No tests found" worker warning. After tracing it properly from a clean vendor checkout, that was wrong for this case — setting failOnPhpunitWarning="false" has no effect. The real cause is the --list-tests sub-invocation described below, which is the same root cause as #1454.
Reproduction
Same suite, ~176 feature test classes, --parallel:
| Condition |
Exit |
pest tests/Feature --shard=1/2 --parallel, no shards.json |
1 ❌ |
pest tests/Feature --shard=1/2 --parallel, fresh shards.json |
0 ✓ |
pest tests/Feature --parallel (no --shard) |
0 ✓ |
When it fails, the error is:
Symfony\Component\Process\Exception\ProcessFailedException
The command "'php' './vendor/bin/pest' 'tests/Feature' '--processes=2'
'--cache-directory' '.../vendor/pestphp/pest/.temp' '--no-output' '--list-tests'" failed.
Exit Code: 2(Misuse of shell builtins)
Output:
INFO Unknown option "--processes". Most similar options are --covers, --colors, ...
Root cause
On the count-based fallback path (no usable shards.json), Pest's sharding enumerates the suite by spawning a child pest ... --list-tests process to build the --filter regex. Under --parallel, that child invocation is passed --processes=N, but --list-tests does not accept --processes, so it exits 2 and Pest's Process::mustRun() throws ProcessFailedException → the whole command exits non-zero.
This is the same underlying bug as #1454 (Unknown option "--processes" when sharding). This issue adds the detail that it is gated by the presence of a complete shards.json: the time-balanced path doesn't hit the broken --list-tests enumeration, so committing a current tests/.pest/shards.json is a reliable workaround — but any newly added class missing from that file silently drops you back onto the failing count-based path.
Likely fix: strip --processes / --parallel from the argv passed to the internal --list-tests sub-invocation (cf. the --parallel-stripping already done in Shard.php::handleArguments).
Versions
- Pest: 4.7.2
- PHPUnit: 12.5.28
- ParaTest: 7.20.0
- PHP: 8.5.4
What happened?
Running
--shard=N/Mtogether with--parallelexits non-zero even though all tests pass — but only when atests/.pest/shards.jsontimings file is absent or doesn't cover the sharded classes (i.e. the count-based fallback path). With a complete, freshshards.json(time-balanced path), the same command exits 0.Reproduction
Same suite, ~176 feature test classes,
--parallel:pest tests/Feature --shard=1/2 --parallel, noshards.jsonpest tests/Feature --shard=1/2 --parallel, freshshards.jsonpest tests/Feature --parallel(no--shard)When it fails, the error is:
Root cause
On the count-based fallback path (no usable
shards.json), Pest's sharding enumerates the suite by spawning a childpest ... --list-testsprocess to build the--filterregex. Under--parallel, that child invocation is passed--processes=N, but--list-testsdoes not accept--processes, so it exits 2 and Pest'sProcess::mustRun()throwsProcessFailedException→ the whole command exits non-zero.This is the same underlying bug as #1454 (
Unknown option "--processes"when sharding). This issue adds the detail that it is gated by the presence of a completeshards.json: the time-balanced path doesn't hit the broken--list-testsenumeration, so committing a currenttests/.pest/shards.jsonis a reliable workaround — but any newly added class missing from that file silently drops you back onto the failing count-based path.Likely fix: strip
--processes/--parallelfrom the argv passed to the internal--list-testssub-invocation (cf. the--parallel-stripping already done inShard.php::handleArguments).Versions