Bug Description
In PHP 8.0+, catch clause variables are unset after the catch block exits. In the submitMessages rejection callback, the catch variable is named $exception — the same name as the closure parameter. When the catch block runs, it shadows and then unsets the closure parameter. The throw $exception after the catch block then fails with Error: Undefined variable $exception.
Affected Code
src/Flowmailer.php — rejection callback inside submitMessages:
function ($exception) use ($guzzlePromise) { // ← closure parameter
try {
if ($exception instanceof ClientErrorException) {
$responseError = $this->handleResponseError($exception->getResponse());
$guzzlePromise->reject($responseError);
return $exception->getResponse();
} else {
$guzzlePromise->reject($exception);
}
} catch (\Exception $exception) { // ← shadows + overwrites closure parameter
$guzzlePromise->reject($exception);
}
// PHP 8.0+: $exception is now UNSET (catch variable scope ends here)
throw $exception; // ← Error: Undefined variable $exception
}
Root Cause
PHP 8.0 changed the scoping of catch clause variables — they are unset when the catch block exits. Because the catch variable has the same name ($exception) as the closure parameter, the parameter is overwritten and then destroyed when the catch block ends. PHP 8.0 migration reference.
Fix
Rename the catch variable so it does not shadow the closure parameter:
- } catch (\Exception $exception) {
- $guzzlePromise->reject($exception);
+ } catch (\Exception $caughtError) {
+ $guzzlePromise->reject($caughtError);
}
Impact
- Email deliveries are silently marked as failed with a misleading error message:
"Flowmailer email sending failed: Undefined variable $exception" instead of the real underlying error
- The
MessageSendingFailed event fires and the recipient never receives their email
- Occurs when the promise rejection path is triggered (e.g. network errors, auth failures)
Environment
- PHP: 8.4.x (confirmed in production), but affects any PHP 8.0+
- SDK version: 2.3.1 (latest)
- Triggered via:
submitMessages() → async promise rejection callback
Bug Description
In PHP 8.0+, catch clause variables are unset after the catch block exits. In the
submitMessagesrejection callback, the catch variable is named$exception— the same name as the closure parameter. When the catch block runs, it shadows and then unsets the closure parameter. Thethrow $exceptionafter the catch block then fails withError: Undefined variable $exception.Affected Code
src/Flowmailer.php— rejection callback insidesubmitMessages:Root Cause
PHP 8.0 changed the scoping of catch clause variables — they are unset when the catch block exits. Because the catch variable has the same name (
$exception) as the closure parameter, the parameter is overwritten and then destroyed when the catch block ends. PHP 8.0 migration reference.Fix
Rename the catch variable so it does not shadow the closure parameter:
Impact
"Flowmailer email sending failed: Undefined variable $exception"instead of the real underlying errorMessageSendingFailedevent fires and the recipient never receives their emailEnvironment
submitMessages()→ async promise rejection callback