Laravel's bail rule stops validating a field the moment one rule fails. Without it, a single bad email can return three errors at once - required, format, and uniqueness - when only the first one matters. This guide covers exactly what bail does, how it differs from stopOnFirstFailure, and the one situation where you should leave it off.
Key Takeaway
Put bail first in the rule list for fields where later rules are meaningless without the earlier ones passing. Skip it on user-facing registration forms where showing every problem at once saves the user a round trip.
What Does Bail Do in Laravel Validation?
Rules for a field run in order, and by default every failing rule reports. Compare the two behaviors for an empty, malformed email:
// Without bail: up to three messages for one field
'email' => ['required', 'email', 'unique:users,email']
// With bail: stops after the first failure
'email' => ['bail', 'required', 'email', 'unique:users,email']With bail first, an empty submission returns only "The email field is required." The email format check and the unique database query never run. That last part matters for performance: unique and exists hit the database, so bailing before them skips queries that cannot change the outcome. Never validate against the database with a value you already know is malformed.
Bail applies per attribute. In an array like items.*.price, each element bails independently - a failure on item 3 does not stop item 4 from being checked. If you need the whole request to stop at the first failure anywhere, that is a different tool.
How Is Bail Different From stopOnFirstFailure?
bail stops rule evaluation for one field. Validator::stopOnFirstFailure() stops the entire validation run at the first failure across all fields:
$validator = Validator::make($request->all(), [
'email' => ['bail', 'required', 'email', 'unique:users,email'],
'coupon' => ['nullable', 'string', 'size:12'],
]);
if ($validator->stopOnFirstFailure()->fails()) {
// $validator->errors() holds exactly one message
return response()->json(['errors' => $validator->errors()], 422);
}Use bail for everyday per-field short-circuiting. Reserve stopOnFirstFailure for machine-to-machine endpoints where the client fixes errors one at a time and extra messages are pure noise - webhook receivers, import pipelines, and internal service calls. Human-facing forms almost always want the full error list, which brings us to the exception.
When Should You Leave Bail Off?
A registration form with a bad email, a short password, and an unchecked terms box should say all three things at once. With bail on every field, the user submits, fixes one field, resubmits, and discovers the next problem - three round trips instead of one. The rule of thumb:
Use bail when later rules depend on earlier ones passing:
requiredbeforeemail,numericbeforemin:0, any format check before a database-backeduniqueorexists.Skip bail when errors are independent and the user benefits from seeing them together: signup forms, settings pages, multi-field wizards.
API consumers that map server errors onto form fields also prefer the complete set - one 422 response they can render wholesale beats a loop of fix-and-resubmit calls. For the broader picture of where validation belongs and how to structure reusable rules, see Laravel validation best practices.
How Do You Test Bail Behavior?
Bail changes the shape of your 422 responses, so pin it with a feature test. Submit input that fails two rules on one field and assert only the first error comes back:
public function test_bail_returns_only_the_first_error(): void
{
$response = $this->postJson('/api/invites', ['email' => 'not-an-email']);
$response->assertStatus(422)
->assertJsonValidationErrors(['email'])
->assertJsonPath('errors.email', [
'The email field must be a valid email address.',
]);
}Then add the mirror test without bail where you expect multiple messages, so a future edit that reorders rules fails loudly instead of silently changing your API contract. The testing patterns behind these assertions - going through the full HTTP stack rather than mocking the validator - are covered in Laravel testing that catches real bugs.
Where Do You Put Bail in a Form Request?
Bail belongs first in the rule array inside rules(), before everything else for that field. Position matters because rules evaluate left to right:
public function rules(): array
{
return [
'email' => ['bail', 'required', 'email', 'unique:users,email'],
'coupon' => ['nullable', 'string', 'size:12'],
];
}Keep bail on the fields where a failed precondition makes the rest of the list pointless, and leave it off the fields where independent errors help the user. Mixed lists like the one above - bail on the email, plain rules on the coupon - are the normal, correct shape in production form requests.
