← All results

PHP 8.5 adds an optional trace key to error_get_last()

Group: Cross-analyzer regressions · Category: regression · File: regressions_error_get_last_trace.php

fatal_error_backtraces is on by default. Fatal errors collected at shutdown then carry a debug_backtrace()-shaped frame list under trace. The RFC only populates the key for those fatals, so it is optional; the other four keys (type, message, file, line) stay required. A tool whose stub is still the 8.4 shape reports trace as an unknown offset (InvalidArrayOffset, "expecting file, line, message or type"). That is the miss this file measures. Access goes through ?? so an honest optional-key reading does not also have to warn about a missing offset. Source: vimeo/psalm#11975, phpstan/phpstan-src#6470. Symfony hit the sealed 8.4 shape in symfony/symfony#66145. References: - https://wiki.php.net/rfc/error_backtraces_v2 - psalm#11975 - phpstan-src#6470

Analyzer results

AnalyzerVersionResultDiagnostics
phan 6.0.11Phan 6.0.11 php-ast version 1.1.3 PHP version used to run Phan: 8.5.11 Pass
regressions_error_get_last_trace.php:53: Invalid offset "nope" of $error of array type array{type:int,message:string,file:string,line:int} [PhanTypeInvalidDimOffset]
phpstan 2.3.5PHPStan - PHP Static Analysis Tool 2.3.5 Pass reported Lv.3+
regressions_error_get_last_trace.php:53: Offset 'nope' does not exist on array{type: int, message: string, file: string, line: int, trace?: list<array{function: string, line?: int, file?: string, class?: class-string, type?: '->'|'::', args?: list<mixed>, object?: object}>}. [reported-from-level=3]
With strict-rules
regressions_error_get_last_trace.php:53: Offset 'nope' does not exist on array{type: int, message: string, file: string, line: int, trace?: list<array{function: string, line?: int, file?: string, class?: class-string, type?: '->'|'::', args?: list<mixed>, object?: object}>}.
psalm 6.20.9Psalm 6.20.9@f67170ac3c3b931f5088681e21d6c8babc601c08
next: 7.0.4
Pass (pzoom≠)
regressions_error_get_last_trace.php:53: Cannot access value on variable $error using offset value of 'nope', expecting 'file', 'line', 'message', 'trace' or 'type' [InvalidArrayOffset]
pzoom (Psalm port)
regressions_error_get_last_trace.php:52: Invalid array offset type: 'trace' [InvalidArrayOffset]
regressions_error_get_last_trace.php:53: Invalid array offset type: 'nope' [InvalidArrayOffset]
mago 1.59.0mago 1.59.0 Fail
regressions_error_get_last_trace.php:52: Impossible `isset` check on key `'trace'` accessed on `array{'file': string, 'line': int, 'message': string, 'type': int}`. [impossible-nonnull-entry-check]
regressions_error_get_last_trace.php:52: Redundant null coalesce: left-hand side is always `null`. [redundant-null-coalesce]
regressions_error_get_last_trace.php:53: Undefined array key 'nope' accessed on `array{'file': string, 'line': int, 'message': string, 'type': int}`. [undefined-string-array-index]
Expectation diff
Line 52: Expected silence (quiet probe), got ["Impossible `isset` check on key `'trace'` accessed on `array{'file': string, 'line': int, 'message': string, 'type': int}`. [impossible-nonnull-entry-check]","Redundant null coalesce: left-hand side is always `null`. [redundant-null-coalesce]"]
mir 0.91.0mir 0.91.0 Pass

No diagnostics reported.

phpantom 0.11.1phpantom_lsp 0.11.1 Pass

No diagnostics reported.

intelephense 1.18.5intelephense 1.18.5 Pass

No diagnostics reported.

phpactor 2026.10.07.0Phpactor 2026.10.07.0 Pass

No diagnostics reported.

phpy 1.0.19490phpy 1.0.19490 Pass

No diagnostics reported.

qodana 262.11879.104PhpStorm 262.11879.104 Fail
regressions_error_get_last_trace.php:52: Value should be one of: "type", "message", "file", "line" [PhpArrayKeyDoesNotMatchArrayShapeInspection]
regressions_error_get_last_trace.php:53: Value should be one of: "type", "message", "file", "line" [PhpArrayKeyDoesNotMatchArrayShapeInspection]
Expectation diff
Line 52: Expected silence (quiet probe), got ["Value should be one of: \"type\", \"message\", \"file\", \"line\" [PhpArrayKeyDoesNotMatchArrayShapeInspection]"]
noverify 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 Pass

No diagnostics reported.

steins 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) Pass

No diagnostics reported.

phpstan-strict 2.3.5PHPStan - PHP Static Analysis Tool 2.3.5 Pass
regressions_error_get_last_trace.php:53: Offset 'nope' does not exist on array{type: int, message: string, file: string, line: int, trace?: list<array{function: string, line?: int, file?: string, class?: class-string, type?: '->'|'::', args?: list<mixed>, object?: object}>}.
psalm-next 7.0.4Psalm 7.0.4@fa5b1f6e665cb743838ffd865b8fc96207f9409f Pass
regressions_error_get_last_trace.php:53: Cannot access value on variable $error using offset value of 'nope', expecting 'file', 'line', 'message', 'trace' or 'type' [InvalidArrayOffset]

Source

<?php

declare(strict_types=1);

namespace Conformance\Tests\RegressionsErrorGetLastTrace;

/**
 * PHP 8.5 adds an optional `trace` key to `error_get_last()`.
 *
 * `fatal_error_backtraces` is on by default. Fatal errors collected at shutdown
 * then carry a `debug_backtrace()`-shaped frame list under `trace`. The RFC
 * only populates the key for those fatals, so it is optional; the other four
 * keys (`type`, `message`, `file`, `line`) stay required.
 *
 * A tool whose stub is still the 8.4 shape reports `trace` as an unknown
 * offset (`InvalidArrayOffset`, "expecting file, line, message or type"). That
 * is the miss this file measures. Access goes through `??` so an honest
 * optional-key reading does not also have to warn about a missing offset.
 *
 * Source: vimeo/psalm#11975, phpstan/phpstan-src#6470. Symfony hit the sealed
 * 8.4 shape in symfony/symfony#66145.
 *
 * References:
 * - https://wiki.php.net/rfc/error_backtraces_v2
 * - https://github.com/vimeo/psalm/pull/11975
 * - https://github.com/phpstan/phpstan-src/pull/6470
 */

function takesInt(int $value): void
{
}

function takesString(string $value): void
{
}

function takesMixed(mixed $value): void
{
}

function inspectLastError(): void
{
    $error = error_get_last();
    if ($error === null) {
        return;
    }

    takesMixed($error['type']); // V: required 8.4 key
    takesMixed($error['message']); // V
    takesMixed($error['file']); // V
    takesMixed($error['line']); // V
    takesMixed($error['trace'] ?? null); // Q: PHP 8.5 optional key — must not be an unknown offset
    takesMixed($error['nope']); // E?: unknown key — tools with an unsealed bag stay silent
}

inspectLastError();