Description
Notification emails sometimes render the wrong title in the email body (the <h2> heading) — not the subject line, which is correct. The wrong heading is borrowed from a different email type sent earlier, e.g. "Reset Your Password" or "Flarum Email Test" appearing on a notification whose heading should read "Notification". The rest of the email is correct.
Reported by a user on the community forum, and reproduced by a maintainer across several emails.
Root cause
The email title leaks via the shared, singleton view factory.
view is a singleton, so it's long-lived within a PHP process. Laravel's Illuminate\View\Factory::share() writes per key into $this->shared and never clears keys that aren't re-shared.
SendInformationalEmailJob — used by password reset, the admin "Send test email", and email confirmation/activation — shares a title onto that singleton:
// src/Mail/Job/SendInformationalEmailJob.php:48
$view->share(compact('forumTitle', 'userEmail', 'title', 'username'));
(title = "Reset Your Password", "Flarum Email Test", etc.)
NotificationMailer renders the notification without a title key:
// src/Notification/NotificationMailer.php:49-55
$data = compact('blueprint', 'user', 'unsubscribeLink', 'settingsLink', 'type', 'forumTitle', 'username', 'userEmail'); // no 'title'
$this->view->share($data);
$this->mailer->send($this->getEmailViews($blueprint), $data, ...);
- The notification template falls back to a default only if
$title is unset:
{{-- views/email/html/notification.blade.php:3 --}}
<h2>{{ $title ?? $translator->trans('core.email.notification.default_title') }}</h2>
With a stale shared title still on the factory, $title is truthy → the ?? default ("Notification") never fires → the notification shows the previous email's title.
Why it's intermittent
It only occurs when a notification email is rendered in the same PHP process, after an informational email (password reset / test email / account activation) — i.e. queue ordering within a single worker run. A fresh process, or a notification rendered first, shows the correct title.
Reproduction (test)
A passing integration test that proves the leak using the real singleton view factory and the real notification blade — framework/core/tests/integration/mail/EmailTitleLeakTest.php:
#[Test]
public function notification_title_leaks_from_a_previously_sent_informational_email(): void
{
$view = $this->app()->getContainer()->make(Factory::class);
// SendInformationalEmailJob (e.g. password reset) shares a title onto the singleton factory:
$view->share(['title' => 'Reset Your Password']);
// NotificationMailer then renders a notification with data that has no `title`:
$data = $this->notificationData(); // no 'title' key
$view->share($data);
$html = $view->make('mail::html.notification', $data)->render();
// BUG: heading is the leaked title, not the default "Notification".
$this->assertSame('Reset Your Password', $this->renderedHeading($html));
}
A companion test confirms that, rendered in isolation, the heading is correctly "Notification".
Possible fix (needs confirmation)
Likely candidates — to be decided when fixing:
- Have
NotificationMailer always provide its own title in the render data, so it can never inherit a stale shared value; and/or
- Stop
SendInformationalEmailJob polluting the shared factory — Mailer::send($view, $data, …) already passes $data to the per-send render, so the view->share(...) there looks redundant.
Either way the reproduction test's expected heading flips back to "Notification", so it doubles as a regression guard. Worth checking whether other shared keys (e.g. username, forumTitle) have the same cross-email bleed.
Environment
flarum/core 2.x. Not specific to any release.
Description
Notification emails sometimes render the wrong title in the email body (the
<h2>heading) — not the subject line, which is correct. The wrong heading is borrowed from a different email type sent earlier, e.g. "Reset Your Password" or "Flarum Email Test" appearing on a notification whose heading should read "Notification". The rest of the email is correct.Reported by a user on the community forum, and reproduced by a maintainer across several emails.
Root cause
The email title leaks via the shared, singleton view factory.
viewis a singleton, so it's long-lived within a PHP process. Laravel'sIlluminate\View\Factory::share()writes per key into$this->sharedand never clears keys that aren't re-shared.SendInformationalEmailJob— used by password reset, the admin "Send test email", and email confirmation/activation — shares atitleonto that singleton:title= "Reset Your Password", "Flarum Email Test", etc.)NotificationMailerrenders the notification without atitlekey:$titleis unset:titlestill on the factory,$titleis truthy → the??default ("Notification") never fires → the notification shows the previous email's title.Why it's intermittent
It only occurs when a notification email is rendered in the same PHP process, after an informational email (password reset / test email / account activation) — i.e. queue ordering within a single worker run. A fresh process, or a notification rendered first, shows the correct title.
Reproduction (test)
A passing integration test that proves the leak using the real singleton view factory and the real notification blade —
framework/core/tests/integration/mail/EmailTitleLeakTest.php:A companion test confirms that, rendered in isolation, the heading is correctly
"Notification".Possible fix (needs confirmation)
Likely candidates — to be decided when fixing:
NotificationMaileralways provide its owntitlein the render data, so it can never inherit a stale shared value; and/orSendInformationalEmailJobpolluting the shared factory —Mailer::send($view, $data, …)already passes$datato the per-send render, so theview->share(...)there looks redundant.Either way the reproduction test's expected heading flips back to
"Notification", so it doubles as a regression guard. Worth checking whether other shared keys (e.g.username,forumTitle) have the same cross-email bleed.Environment
flarum/core2.x. Not specific to any release.