diff --git a/core/lib/Drupal/Component/Gettext/PoStreamReader.php b/core/lib/Drupal/Component/Gettext/PoStreamReader.php
index 46a18498aee6680159d432a16871b80d89f877e5..875abc86190a0c3451abc553e4941a97ba3e935f 100644
--- a/core/lib/Drupal/Component/Gettext/PoStreamReader.php
+++ b/core/lib/Drupal/Component/Gettext/PoStreamReader.php
@@ -150,13 +150,14 @@ public function setURI($uri) {
    *   If the URI is not yet set.
    */
   public function open() {
-    if (!empty($this->uri)) {
-      $this->fd = fopen($this->uri, 'rb');
-      $this->readHeader();
-    }
-    else {
+    if (empty($this->uri)) {
       throw new \Exception('Cannot open stream without URI set.');
     }
+    $this->fd = @fopen($this->uri, 'rb');
+    if (!$this->fd) {
+      throw new \Exception('Cannot open stream for uri ' . $this->uri);
+    }
+    $this->readHeader();
   }
 
   /**
@@ -245,7 +246,7 @@ private function readHeader() {
   private function readLine() {
     // Read a line and set the stream finished indicator if it was not
     // possible anymore.
-    $line = fgets($this->fd);
+    $line = $this->fd ? fgets($this->fd) : FALSE;
     $this->finished = ($line === FALSE);
 
     if (!$this->finished) {
diff --git a/core/tests/Drupal/Tests/Component/Gettext/PoStreamReaderTest.php b/core/tests/Drupal/Tests/Component/Gettext/PoStreamReaderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d74f1f3801dd606b1173af1c028e07b8bd8d40b
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Gettext/PoStreamReaderTest.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\Component\Gettext;
+
+use Drupal\Component\Gettext\PoStreamReader;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Unit tests for the Gettext PO file header handling features.
+ *
+ * @see Drupal\Component\Gettext\PoHeader.
+ *
+ * @group Gettext
+ */
+class PoStreamReaderTest extends TestCase {
+
+  /**
+   * Creates and returns a PoStreamReader instance with a fake URI.
+   *
+   * @return \Drupal\Component\Gettext\PoStreamReader
+   *   The PoStreamReader instance.
+   */
+  private function createPoStreamReader(): PoStreamReader {
+    $reader = new PoStreamReader();
+    $reader->setURI('fake');
+    return $reader;
+  }
+
+  /**
+   * Calling open should throw an exception if URI is invalid.
+   *
+   * See issue #3301239.
+   */
+  public function testOpenMethodThrowsExceptionOnInvalidURI(): void {
+    $reader = $this->createPoStreamReader();
+    $this->expectException(\Exception::class);
+    $reader->open();
+  }
+
+  /**
+   * Validates that calling readItem with a NULL file descriptor returns NULL.
+   *
+   * See issue #3301239.
+   */
+  public function testOpeningFileError(): void {
+    $reader = $this->createPoStreamReader();
+    $this->assertNull($reader->readItem());
+  }
+
+}