Skip to content

Prevent reporting failures and restore fatal error capture - #77

Merged
pavelzotikov merged 4 commits into
masterfrom
fix/fatal-error-reporting
Nov 1, 2026
Merged

pavelzotikov merged 4 commits into
masterfrom
fix/fatal-error-reporting

Conversation

@pavelzotikov

Copy link
Copy Markdown
Contributor

Prevent transport failures and recursive reporting from interrupting the application.
Fix fatal-error filtering during shutdown and avoid duplicate reports.

@pavelzotikov
pavelzotikov requested a review from neSpecc as a code owner November 1, 2026 13:40

@neSpecc neSpecc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fatal filter looks correct. The old && chain used the bitmask as a boolean, so any error_get_last() skipped the report. Shutdown tests cover the fatal, the warning and the duplicate exception.

handleException() still sets disableFatalErrorHandler for the rest of the process when it rethrows, and Catcher::sendException() always rethrows. After the caller catches that exception, later fatals in the same process are dropped. Clear the flag once the rethrow is caught outside, or skip it for a manual sendException().

Comment thread src/Handler.php
if ($event !== null) {
$this->send($event);
}
$this->captureSafely(function () use ($exception, $context): void {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isCapturing only wraps the send. On a nested handleException() captureSafely() returns immediately, then this method still clears previousExceptionHandler, sets disableFatalErrorHandler and throws.

That happens if beforeSend or the transport calls handleException() while a report is in progress: the nested event is dropped and fatal capture stays off for the rest of the process. sendEvent() already returns early in this situation. Same guard belongs at the top of handleException(), before the previous-handler tail. A test next to testBeforeSendCannotRecursivelyCapture would lock it.

Comment thread src/Handler.php
if ($event !== null) {
$this->send($event);
}
$this->captureSafely(function () use ($error): void {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E_ERROR skips finally, so a fatal in the middle of a report leaves isCapturing true. captureSafely() then returns and this fatal is never sent.

Shutdown does not resume the interrupted capture. Reset the flag at the start of handleFatal().

Comment thread src/Handler.php
$this->transport->send($event);
try {
$this->transport->send($event);
} catch (\Throwable $exception) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every Throwable from the transport is swallowed here, including a missing curl extension. sendMessage() and sendEvent() then return as if delivery worked.

Keeping the application running is right. Log the failure with error_log so a broken transport is visible.

@pavelzotikov
pavelzotikov merged commit cb607fc into master Nov 1, 2026
5 checks passed
@pavelzotikov
pavelzotikov deleted the fix/fatal-error-reporting branch November 1, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants