diff --git a/typo3/sysext/scheduler/Classes/Command/SchedulerCommand.php b/typo3/sysext/scheduler/Classes/Command/SchedulerCommand.php
index 64a502c865148b11b4821394c3038fc8e9d2198d..a207b3d6538872af4fe67353b171b5eba49c61be 100644
--- a/typo3/sysext/scheduler/Classes/Command/SchedulerCommand.php
+++ b/typo3/sysext/scheduler/Classes/Command/SchedulerCommand.php
@@ -30,11 +30,6 @@ use TYPO3\CMS\Scheduler\Task\AbstractTask;
  */
 class SchedulerCommand extends Command
 {
-    /**
-     * @var bool
-     */
-    protected $hasTask = true;
-
     /**
      * @var Scheduler
      */
@@ -122,8 +117,7 @@ Call it like this: typo3/sysext/core/bin/typo3 scheduler:run --task=13 -f')
 
         $this->forceExecution = (bool)$input->getOption('force');
         $this->stopTasks = $this->shouldStopTasks((bool)$input->getOption('stop'));
-        $this->loopTasks();
-        return 0;
+        return $this->loopTasks() ? 0 : 1;
     }
 
     /**
@@ -184,38 +178,37 @@ Call it like this: typo3/sysext/core/bin/typo3 scheduler:run --task=13 -f')
     /**
      * Execute tasks in loop that are ready to execute
      */
-    protected function loopTasks()
+    protected function loopTasks(): bool
     {
+        $hasError = false;
         do {
+            $task = null;
             // Try getting the next task and execute it
             // If there are no more tasks to execute, an exception is thrown by \TYPO3\CMS\Scheduler\Scheduler::fetchTask()
             try {
                 $task = $this->fetchNextTask();
+                if ($task === null) {
+                    break;
+                }
                 try {
                     $this->executeOrStopTask($task);
                 } catch (\Exception $e) {
-                    if ($this->io->isVerbose()) {
-                        $this->io->warning($e->getMessage());
-                    }
+                    $this->io->getErrorStyle()->error($e->getMessage());
+                    $hasError = true;
                     // We ignore any exception that may have been thrown during execution,
                     // as this is a background process.
                     // The exception message has been recorded to the database anyway
                     continue;
                 }
-            } catch (\OutOfBoundsException $e) {
-                if ($this->io->isVeryVerbose()) {
-                    $this->io->writeln($e->getMessage());
-                }
-                $this->hasTask = !empty($this->overwrittenTaskList);
             } catch (\UnexpectedValueException $e) {
-                if ($this->io->isVerbose()) {
-                    $this->io->warning($e->getMessage());
-                }
+                $this->io->getErrorStyle()->error($e->getMessage());
+                $hasError = true;
                 continue;
             }
-        } while ($this->hasTask);
+        } while ($task !== null);
         // Record the run in the system registry
         $this->scheduler->recordLastRun();
+        return !$hasError;
     }
 
     /**
@@ -224,18 +217,16 @@ Call it like this: typo3/sysext/core/bin/typo3 scheduler:run --task=13 -f')
      *
      * Without the --task option we ask the scheduler for the next task with pending execution.
      *
-     * @return AbstractTask
-     * @throws \OutOfBoundsException When there are no more tasks to execute.
      * @throws \UnexpectedValueException When no task is found by the provided UID or the task is not marked for execution.
      */
-    protected function fetchNextTask(): AbstractTask
+    protected function fetchNextTask(): ?AbstractTask
     {
         if ($this->overwrittenTaskList === null) {
             return $this->scheduler->fetchTask();
         }
 
         if (count($this->overwrittenTaskList) === 0) {
-            throw new \OutOfBoundsException('No more tasks to execute', 1547675594);
+            return null;
         }
 
         $taskUid = (int)array_shift($this->overwrittenTaskList);
diff --git a/typo3/sysext/scheduler/Classes/Scheduler.php b/typo3/sysext/scheduler/Classes/Scheduler.php
index 1afa55349a018e4bf27f77dc0fd820ecbd810d10..24bfde3500f9f25c526b3ab1e240eeffaa0e8a1d 100644
--- a/typo3/sysext/scheduler/Classes/Scheduler.php
+++ b/typo3/sysext/scheduler/Classes/Scheduler.php
@@ -305,11 +305,11 @@ class Scheduler implements SingletonInterface
      * If there are no due tasks the method throws an exception.
      *
      * @param int $uid Primary key of a task
-     * @return Task\AbstractTask The fetched task object
+     * @return Task\AbstractTask|null The fetched task object
      * @throws \OutOfBoundsException
      * @throws \UnexpectedValueException
      */
-    public function fetchTask($uid = 0)
+    public function fetchTask($uid = 0): ?AbstractTask
     {
         $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
         $queryBuilder = $connectionPool->getQueryBuilderForTable('tx_scheduler_task');
@@ -352,7 +352,7 @@ class Scheduler implements SingletonInterface
         if (empty($row)) {
             if (empty($uid)) {
                 // No uid was passed and no overdue task was found
-                throw new \OutOfBoundsException('No (more) tasks available for execution', 1247827244);
+                return null;
             }
             // Although a uid was passed, no task with given was found
             throw new \OutOfBoundsException('No task with id ' . $uid . ' found', 1422044826);