Skip to content
PHP News
Search
Implemented PHP 8.6

Stream Error Handling Improvements

Adds exception and silent modes for stream errors, with structured StreamError objects and a StreamErrorCode enum you can inspect.

Should the Stream Error Handling Improvements be added to PHP core?

Primary vote · 2/3 majority

25 Yes 1 No 5 abstain 96% approval

This poll has closed.

It passed 25 to 1, with 5 abstaining, against a two-thirds requirement. Voting ran from May 4 to May 18, 2026. It targets PHP 8.6, and the page lists it as implemented.

Summary

Streams are how PHP reads and writes files, URLs and sockets, and when they fail, they don't all fail the same way. Some raise warnings, some raise notices, and the messages carry little detail. This RFC from Jakub Zelenka adds one consistent way to handle stream errors: you can get exceptions, or stay silent and read the errors later as objects.

Before and after

Today, catching a failed fopen() usually means dealing with warnings and a custom error handler. With this RFC, you can ask for an exception through the stream context:

$context = stream_context_create([
    'stream' => [
        'error_mode' => StreamErrorMode::Exception,
    ]
]);

try {
    $stream = fopen('/nonexistent/file.txt', 'r', false, $context);
} catch (StreamException $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

The new options

Three new context options go under stream:

  • error_mode picks how errors surface. StreamErrorMode::Error keeps today's warnings and is the default, Exception throws a StreamException when an operation can't complete, and Silent reports nothing.
  • error_store picks which errors PHP keeps for later, which you read with stream_last_errors().
  • error_handler is a callback that receives the errors as an array.

Each error is a StreamError object. It has a code from a new StreamErrorCode enum, like NotFound or PermissionDenied, along with a message, the wrapper name, and usually the file name or URL. One operation can produce several errors, so you get them as an array with the primary error first.

stream_select(), stream_copy_to_stream(), stream_socket_pair() and stream_is_local() gain an optional $context parameter so they can use these options.

You can't set these options on the default context with stream_context_set_default(), because that throws a ValueError. The RFC says this protects libraries that expect the usual warnings.

What it means for existing code

The default mode stays the same, so your current warnings don't change. The RFC lists a few small differences. Some errors that were reported incorrectly are fixed, child streams now inherit the context, and errors are reported closer to when the function returns, so their order relative to other errors might change.