diff --git a/package_manager/src/EventSubscriber/ExcludedPathsSubscriber.php b/package_manager/src/EventSubscriber/ExcludedPathsSubscriber.php
index a54a4e2816e77c549c27aca46f6a574600507465..6b781d44e4ef70200975049df6645115b791243b 100644
--- a/package_manager/src/EventSubscriber/ExcludedPathsSubscriber.php
+++ b/package_manager/src/EventSubscriber/ExcludedPathsSubscriber.php
@@ -189,7 +189,8 @@ class ExcludedPathsSubscriber implements EventSubscriberInterface {
       ->directories()
       ->name('.git')
       ->ignoreVCS(FALSE)
-      ->ignoreDotFiles(FALSE);
+      ->ignoreDotFiles(FALSE)
+      ->ignoreUnreadableDirs();
 
     foreach ($finder as $git_directory) {
       $project[] = $git_directory->getPathname();
diff --git a/package_manager/tests/src/Kernel/ExcludedPathsTest.php b/package_manager/tests/src/Kernel/ExcludedPathsTest.php
index 33dd8f5e169025836ddd8bd21efcb2c9fbfc5428..79071491d1278ecfc5191de0a71e40f1588935f2 100644
--- a/package_manager/tests/src/Kernel/ExcludedPathsTest.php
+++ b/package_manager/tests/src/Kernel/ExcludedPathsTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\Core\Database\Connection;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\EventSubscriber\ExcludedPathsSubscriber;
 
@@ -14,18 +15,19 @@ use Drupal\package_manager\EventSubscriber\ExcludedPathsSubscriber;
 class ExcludedPathsTest extends PackageManagerKernelTestBase {
 
   /**
-   * The mocked SQLite database connection.
-   *
-   * @var \Drupal\Core\Database\Connection|\Prophecy\Prophecy\ObjectProphecy
+   * {@inheritdoc}
    */
-  private $mockDatabase;
+  public function register(ContainerBuilder $container) {
+    parent::register($container);
+
+    $container->getDefinition('package_manager.excluded_paths_subscriber')
+      ->setClass(TestExcludedPathsSubscriber::class);
+  }
 
   /**
-   * {@inheritdoc}
+   * Tests that certain paths are excluded from staging operations.
    */
-  protected function setUp(): void {
-    parent::setUp();
-
+  public function testExcludedPaths(): void {
     // The private stream wrapper is only registered if this setting is set.
     // @see \Drupal\Core\CoreServiceProvider::register()
     $this->setSetting('file_private_path', 'private');
@@ -38,16 +40,6 @@ class ExcludedPathsTest extends PackageManagerKernelTestBase {
     // Ensure we have an up-to-date container.
     $this->container = $this->container->get('kernel')->getContainer();
 
-    // Mock a SQLite database connection so we can test that the subscriber will
-    // exclude the database files.
-    $this->mockDatabase = $this->prophesize(Connection::class);
-    $this->mockDatabase->driver()->willReturn('sqlite');
-  }
-
-  /**
-   * Tests that certain paths are excluded from staging operations.
-   */
-  public function testExcludedPaths(): void {
     $this->createTestProject();
     $active_dir = $this->container->get('package_manager.path_locator')
       ->getActiveDirectory();
@@ -59,10 +51,17 @@ class ExcludedPathsTest extends PackageManagerKernelTestBase {
 
     // Mock a SQLite database connection to a file in the active directory. The
     // file should not be staged.
-    $this->mockDatabase->getConnectionOptions()->willReturn([
+    $database = $this->prophesize(Connection::class);
+    $database->driver()->willReturn('sqlite');
+    $database->getConnectionOptions()->willReturn([
       'database' => $site_path . '/db.sqlite',
     ]);
-    $this->setUpSubscriber($site_path);
+
+    // Update the event subscriber's dependencies.
+    /** @var \Drupal\Tests\package_manager\Kernel\TestExcludedPathsSubscriber $subscriber */
+    $subscriber = $this->container->get('package_manager.excluded_paths_subscriber');
+    $subscriber->sitePath = $site_path;
+    $subscriber->database = $database->reveal();
 
     $stage = $this->createStage();
     $stage->create();
@@ -162,13 +161,11 @@ class ExcludedPathsTest extends PackageManagerKernelTestBase {
   /**
    * Tests that SQLite database paths are excluded from the staging area.
    *
-   * The exclusion of SQLite databases from the staging area is functionally
-   * tested by \Drupal\Tests\package_manager\Functional\ExcludedPathsTest. The
-   * purpose of this test is to ensure that SQLite database paths are processed
-   * properly (e.g., converting an absolute path to a relative path) before
-   * being flagged for exclusion.
+   * This test ensures that SQLite database paths are processed properly (e.g.,
+   * converting an absolute path to a relative path) before being flagged for
+   * exclusion.
    *
-   * @param string $database
+   * @param string $database_path
    *   The path of the SQLite database, as set in the database connection
    *   options.
    * @param string[] $expected_exclusions
@@ -176,32 +173,60 @@ class ExcludedPathsTest extends PackageManagerKernelTestBase {
    *
    * @dataProvider providerSqliteDatabaseExcluded
    */
-  public function testSqliteDatabaseExcluded(string $database, array $expected_exclusions): void {
-    $this->mockDatabase->getConnectionOptions()->willReturn([
-      'database' => $database,
+  public function testSqliteDatabaseExcluded(string $database_path, array $expected_exclusions): void {
+    $database = $this->prophesize(Connection::class);
+    $database->driver()->willReturn('sqlite');
+    $database->getConnectionOptions()->willReturn([
+      'database' => $database_path,
     ]);
 
+    // Update the event subscriber to use the mocked database.
+    /** @var \Drupal\Tests\package_manager\Kernel\TestExcludedPathsSubscriber $subscriber */
+    $subscriber = $this->container->get('package_manager.excluded_paths_subscriber');
+    $subscriber->database = $database->reveal();
+
     $event = new PreCreateEvent($this->createStage());
-    $this->setUpSubscriber();
-    $this->container->get('package_manager.excluded_paths_subscriber')->ignoreCommonPaths($event);
+    // Invoke the event subscriber directly, so we can check that the database
+    // was correctly excluded.
+    $subscriber->ignoreCommonPaths($event);
     // All of the expected exclusions should be flagged.
     $this->assertEmpty(array_diff($expected_exclusions, $event->getExcludedPaths()));
   }
 
   /**
-   * Sets up the event subscriber with a mocked database and site path.
-   *
-   * @param string $site_path
-   *   (optional) The site path. Defaults to 'sites/default'.
+   * Tests that unreadable directories are ignored by the event subscriber.
    */
-  private function setUpSubscriber(string $site_path = 'sites/default'): void {
-    $this->container->set('package_manager.excluded_paths_subscriber', new ExcludedPathsSubscriber(
-      $site_path,
-      $this->container->get('package_manager.symfony_file_system'),
-      $this->container->get('stream_wrapper_manager'),
-      $this->mockDatabase->reveal(),
-      $this->container->get('package_manager.path_locator')
-    ));
+  public function testUnreadableDirectoriesAreIgnored(): void {
+    $this->createTestProject();
+    $active_dir = $this->container->get('package_manager.path_locator')
+      ->getActiveDirectory();
+
+    // Create an unreadable directory within the active directory, which will
+    // raise an exception as the event subscriber tries to scan for .git
+    // directories...unless unreadable directories are being ignored, as they
+    // should be.
+    $unreadable_dir = $active_dir . '/unreadable';
+    mkdir($unreadable_dir, 0000);
+    $this->assertDirectoryIsNotReadable($unreadable_dir);
+
+    $this->createStage()->create();
   }
 
 }
+
+/**
+ * A test-only version of the excluded paths event subscriber.
+ */
+class TestExcludedPathsSubscriber extends ExcludedPathsSubscriber {
+
+  /**
+   * {@inheritdoc}
+   */
+  public $sitePath;
+
+  /**
+   * {@inheritdoc}
+   */
+  public $database;
+
+}