laravel/framework · error · HttpException
Your email address is not verified.
Error message
Your email address is not verified.
What it means
Produced by EnsureEmailIsVerified middleware via abort(403, 'Your email address is not verified.') — only when the request expects JSON. For non-JSON requests it redirects to the verification.notice route (or a custom $redirectToRoute). The 403 fires when there is no authenticated user OR the user implements MustVerifyEmail and has not verified their email. This is an HTTP-level access-control response, not a thrown exception a caller catches in normal PHP flow.
Source
Thrown at src/Illuminate/Auth/Middleware/EnsureEmailIsVerified.php:37
{
return static::class.':'.$route;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $redirectToRoute
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse|null
*/
public function handle($request, Closure $next, $redirectToRoute = null)
{
if (! $request->user() ||
($request->user() instanceof MustVerifyEmail &&
! $request->user()->hasVerifiedEmail())) {
return $request->expectsJson()
? abort(403, 'Your email address is not verified.')
: Redirect::guest(URL::route($redirectToRoute ?: 'verification.notice'));
}
return $next($request);
}
}
View on GitHub (pinned to bd6b5437e6)
Solutions
- Trigger email verification: send the verification link via the verification.send route (Event dispatch EmailVerificationRequested).
- Mark the user verified manually in tests/dev: $user->markEmailAsVerified();.
- Ensure the route is also behind the 'auth' middleware so $request->user() is non-null.
- For APIs, return a structured JSON error/redirect URL instead of bare abort by customizing the middleware or adding an after-handler.
- Exclude the route from the verified middleware if it must be publicly accessible.
Example fix
// before — routes/web.php
Route::get('/api/profile', fn () => ...)->middleware(['auth', 'verified']);
// after — trigger verification + ensure user is set
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
Route::get('/email/verify', fn () => view('auth.verify'))->name('verification.notice');
Route::post('/email/verification-notification', function (\Illuminate\Http\Request $request) {
$request->user()->sendEmailVerificationNotification();
return back()->with('status', 'verification-link-sent');
})->middleware(['auth', 'throttle:6,1'])->name('verification.send');
// In tests, verify the user first:
$user = User::factory()->create(['email_verified_at' => now()]);
$this->actingAs($user); Defensive patterns
Strategy: validation
Validate before calling
// In a controller/middleware, verify before relying on 'verified':
$user = $request->user();
if (! $user || ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail())) {
return response()->json(['message' => 'Email verification required.', 'verify_url' => route('verification.notice')], 403);
} Type guard
use Illuminate\Contracts\Auth\MustVerifyEmail;
function emailVerifiedForRequest(\Illuminate\Http\Request $request): bool {
$user = $request->user();
return $user !== null
&& (! $user instanceof MustVerifyEmail || $user->hasVerifiedEmail());
} Try / catch
// abort(403) is an HttpResponseException; you cannot catch it as a normal exception in the
// controller that hits the middleware. Catch globally in an API exception handler for structured JSON:
// app/Exceptions/Handler.php
public function register(): void {
$this->renderable(function (\Symfony\Component\HttpKernel\Exception\HttpException $e, $request) {
if ($e->getStatusCode() === 403 && $e->getMessage() === 'Your email address is not verified.' && $request->expectsJson()) {
return response()->json([
'message' => 'Email not verified.',
'action' => 'verify_email',
'resend_url' => route('verification.send'),
], 403);
}
});
} Prevention
- Always send the verification email on registration (dispatch EmailVerificationRequested / ShouldQueue notification).
- In tests, mark users verified: $user->markEmailAsVerified() or User::factory()->create(['email_verified_at' => now()]).
- Pair 'verified' with 'auth' so $request->user() is non-null.
- For APIs, override the 403 render to return a structured JSON error with the resend link.
When it happens
Trigger: Applying the 'verified' middleware aliasus (EnsureEmailIsVerified) to a route and hitting it via an API client that sets Accept: application/json while the user is unverified (or not logged in). Common with SPA/mobile clients that always send Accept JSON.
Common situations: Forgetting to send the email verification after registration. Users who registered but never clicked the verification link. Token/session expired so $request->user() is null. SPA defaults to JSON and never sees the redirect, getting a raw 403 instead.
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/c26565ab26390f5c.json.
Report an issue: GitHub.