diff --git a/Build/phpstan/phpstan-baseline.neon b/Build/phpstan/phpstan-baseline.neon
index 3320a4d3dfc2b0ba48727f28a37757a75e4eaad0..ab7c97a9df5e5ae13c3153ed846f7bccb3b5fef8 100644
--- a/Build/phpstan/phpstan-baseline.neon
+++ b/Build/phpstan/phpstan-baseline.neon
@@ -20,11 +20,6 @@ parameters:
 			count: 1
 			path: ../../typo3/sysext/backend/Classes/Controller/EditDocumentController.php
 
-		-
-			message: "#^Offset 1 on array\\{string, non\\-empty\\-string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: ../../typo3/sysext/backend/Classes/EventListener/FailedLoginAttemptNotification.php
-
 		-
 			message: "#^Offset 'eval' on array on left side of \\?\\? always exists and is not nullable\\.$#"
 			count: 1
@@ -180,16 +175,6 @@ parameters:
 			count: 1
 			path: ../../typo3/sysext/backend/Classes/Utility/BackendUtility.php
 
-		-
-			message: "#^Offset 1 on array\\{string, non\\-empty\\-string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: ../../typo3/sysext/belog/Classes/Domain/Model/LogEntry.php
-
-		-
-			message: "#^Offset 1 on array\\{string, non\\-empty\\-string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: ../../typo3/sysext/belog/Classes/ViewHelpers/FormatDetailsViewHelper.php
-
 		-
 			message: "#^Property TYPO3\\\\CMS\\\\Core\\\\Authentication\\\\AbstractUserAuthentication\\:\\:\\$writeAttemptLog \\(bool\\) on left side of \\?\\? is not nullable\\.$#"
 			count: 1
@@ -280,11 +265,6 @@ parameters:
 			count: 1
 			path: ../../typo3/sysext/core/Classes/Crypto/HashService.php
 
-		-
-			message: "#^Offset 1 on array\\{string, non\\-empty\\-string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: ../../typo3/sysext/core/Classes/DataHandling/DataHandler.php
-
 		-
 			message: "#^Method TYPO3\\\\CMS\\\\Core\\\\DataHandling\\\\Localization\\\\DataMapProcessor\\:\\:fetchDependencies\\(\\) should return array\\<array\\<TYPO3\\\\CMS\\\\Core\\\\DataHandling\\\\Localization\\\\DataMapItem\\>\\> but returns array\\<int\\|string, array\\<string, array\\<int, TYPO3\\\\CMS\\\\Core\\\\DataHandling\\\\Localization\\\\DataMapItem\\>\\>\\>\\.$#"
 			count: 1
@@ -725,11 +705,6 @@ parameters:
 			count: 1
 			path: ../../typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php
 
-		-
-			message: "#^Offset 1 on array\\{string, non\\-empty\\-string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: ../../typo3/sysext/core/Tests/Functional/DataScenarios/AbstractDataHandlerActionTestCase.php
-
 		-
 			message: "#^Parameter \\#2 \\$data of method TYPO3\\\\CMS\\\\Core\\\\Cache\\\\Backend\\\\Typo3DatabaseBackend\\:\\:set\\(\\) expects string, array\\<int, string\\> given\\.$#"
 			count: 1
@@ -1630,16 +1605,6 @@ parameters:
 			count: 1
 			path: ../../typo3/sysext/install/Classes/Updates/DatabaseRowsUpdateWizard.php
 
-		-
-			message: "#^Offset 1 on array\\{string, non\\-empty\\-string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: ../../typo3/sysext/install/Classes/Updates/SysLogSerializationUpdate.php
-
-		-
-			message: "#^Offset 1 on array\\{string, non\\-empty\\-string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: ../../typo3/sysext/lowlevel/Classes/Command/ListSysLogCommand.php
-
 		-
 			message: "#^Negated boolean expression is always true\\.$#"
 			count: 1
@@ -1654,8 +1619,3 @@ parameters:
 			message: "#^Strict comparison using \\=\\=\\= between non\\-falsy\\-string and '' will always evaluate to false\\.$#"
 			count: 1
 			path: ../../typo3/sysext/scheduler/Classes/CronCommand/NormalizeCommand.php
-
-		-
-			message: "#^Offset 1 on array\\{string, non\\-empty\\-string\\} on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: ../../typo3/sysext/workspaces/Classes/Controller/Remote/RemoteServer.php
diff --git a/typo3/sysext/core/Classes/Log/LogDataTrait.php b/typo3/sysext/core/Classes/Log/LogDataTrait.php
index 0c4767b671b47cba20104a4fcb55b28324312593..183e766a79ebdfbeddc37c4c6e06679a8f65ec33 100644
--- a/typo3/sysext/core/Classes/Log/LogDataTrait.php
+++ b/typo3/sysext/core/Classes/Log/LogDataTrait.php
@@ -55,17 +55,21 @@ trait LogDataTrait
      */
     protected static function formatLogDetailsStatic(string $detailString, array $substitutes): string
     {
-        // Handle legacy "%s" placeholders
-        if (str_contains($detailString, '%')) {
+        // Handles placeholders with "%" first
+        try {
             $detailString = vsprintf($detailString, $substitutes);
-        } elseif ($substitutes !== []) {
-            // Handles placeholders with "{myPlaceholder}"
-            $detailString = preg_replace_callback('/{([A-z]+)}/', static function ($matches) use ($substitutes) {
-                // $matches[0] contains the unsubstituted placeholder
-                return $substitutes[$matches[1] ?? null] ?? $matches[0];
-            }, $detailString);
+        } catch (\ValueError|\ArgumentCountError) {
+            // Ignore if $substitutes doesn't contain the number of "%" found in $detailString
         }
-        // Remove possible pending other %s
+
+        // Handles placeholders with "{myPlaceholder}"
+        $detailString = preg_replace_callback('/{([A-z]+)}/', static function (array $matches) use ($substitutes) {
+            // $matches[0] contains the unsubstituted placeholder
+            /** @var array{0: string, 1?: string} $matches added to mitigate false-positives PHPStan reportings */
+            return $substitutes[$matches[1] ?? null] ?? $matches[0];
+        }, $detailString);
+
+        // Remove possible pending %s
         return str_replace('%s', '', (string)$detailString);
     }
 }
diff --git a/typo3/sysext/core/Tests/Unit/Log/Fixtures/LogDataTraitTestAccessor.php b/typo3/sysext/core/Tests/Unit/Log/Fixtures/LogDataTraitTestAccessor.php
new file mode 100644
index 0000000000000000000000000000000000000000..a7b632f63182aacbc3290f66145c96cd8def5c6d
--- /dev/null
+++ b/typo3/sysext/core/Tests/Unit/Log/Fixtures/LogDataTraitTestAccessor.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Tests\Unit\Log\Fixtures;
+
+use TYPO3\CMS\Core\Log\LogDataTrait;
+
+final class LogDataTraitTestAccessor
+{
+    use LogDataTrait;
+
+    public function callUnserializeLogData(mixed $logData): ?array
+    {
+        return $this->unserializeLogData($logData);
+    }
+
+    public function callFormatLogDetails(string $detailString, mixed $substitutes): string
+    {
+        return $this->formatLogDetails($detailString, $substitutes);
+    }
+
+    public static function callFormatLogDetailsStatic(string $detailString, array $substitutes): string
+    {
+        return self::formatLogDetailsStatic($detailString, $substitutes);
+    }
+}
diff --git a/typo3/sysext/core/Tests/Unit/Log/LogDataTraitTest.php b/typo3/sysext/core/Tests/Unit/Log/LogDataTraitTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d05e3365fa3d39b590af8dae346a2d57cc1a0abf
--- /dev/null
+++ b/typo3/sysext/core/Tests/Unit/Log/LogDataTraitTest.php
@@ -0,0 +1,248 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Tests\Unit\Log;
+
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\Test;
+use TYPO3\CMS\Core\Tests\Unit\Log\Fixtures\LogDataTraitTestAccessor;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+final class LogDataTraitTest extends UnitTestCase
+{
+    public static function formatLogDetailsLegacyStringsDataProvider(): \Generator
+    {
+        yield 'String with percent and empty argument %' => [
+            'detailString' => 'String with percent and empty argument %',
+            'substitutes' => [],
+            'expectedResult' => 'String with percent and empty argument %',
+        ];
+        yield 'String with string %s and empty argument' => [
+            'detailString' => 'String with string %s and empty argument',
+            'substitutes' => [],
+            'expectedResult' => 'String with string  and empty argument',
+        ];
+        yield 'String with decimal %d and empty argument' => [
+            'detailString' => 'String with decimal %d and empty argument',
+            'substitutes' => [],
+            'expectedResult' => 'String with decimal %d and empty argument',
+        ];
+        yield 'String with percent and single argument %' => [
+            'detailString' => 'String with percent and single argument %',
+            'substitutes' => [0 => 'a argument'],
+            'expectedResult' => 'String with percent and single argument %',
+        ];
+        yield 'String with string %s and single argument' => [
+            'detailString' => 'String with string %s and single argument',
+            'substitutes' => [0 => 'this is a string'],
+            'expectedResult' => 'String with string this is a string and single argument',
+        ];
+        yield 'String with decimal %d and single argument' => [
+            'detailString' => 'String with decimal %d and single argument',
+            'substitutes' => [0 => 42],
+            'expectedResult' => 'String with decimal 42 and single argument',
+        ];
+        yield 'String with percent and multiple arguments %' => [
+            'detailString' => 'String with percent and multiple arguments %',
+            'substitutes' => [0 => 'a argument', 1 => 'another argument'],
+            'expectedResult' => 'String with percent and multiple arguments %',
+        ];
+        yield 'String with string %s and multiple arguments %s' => [
+            'detailString' => 'String with string %s and multiple arguments %s',
+            'substitutes' => [0 => 'this is a string', 1 => 'another string'],
+            'expectedResult' => 'String with string this is a string and multiple arguments another string',
+        ];
+        yield 'String with decimal %d and multiple arguments %s' => [
+            'detailString' => 'String with decimal %d and multiple arguments %s',
+            'substitutes' => [0 => 42, 1 => 'another string'],
+            'expectedResult' => 'String with decimal 42 and multiple arguments another string',
+        ];
+        yield 'String with percent and to many arguments %' => [
+            'detailString' => 'String with percent and to many arguments %',
+            'substitutes' => [0 => 'a argument', 1 => 'another argument', 2 => 'a third string'],
+            'expectedResult' => 'String with percent and to many arguments %',
+        ];
+        yield 'String with string %s and to many arguments %s' => [
+            'detailString' => 'String with string %s and to many arguments %s',
+            'substitutes' => [0 => 'this is a string', 1 => 'another string', 2 => 'a third string'],
+            'expectedResult' => 'String with string this is a string and to many arguments another string',
+        ];
+        yield 'String with decimal %d and to many arguments %s' => [
+            'detailString' => 'String with decimal %d and to many arguments %s',
+            'substitutes' => [0 => 42, 1 => 'another string', 2 => 'a third string'],
+            'expectedResult' => 'String with decimal 42 and to many arguments another string',
+        ];
+        // %s is special since it's replaced with empty string now matter what
+        yield 'String with string %s %s and empty argument' => [
+            'detailString' => 'String with string %s %s and empty argument',
+            'substitutes' => [],
+            'expectedResult' => 'String with string   and empty argument',
+        ];
+        yield 'String with string %s %s and to few arguments' => [
+            'detailString' => 'String with string %s %s and to few arguments',
+            'substitutes' => [0 => 'astring'],
+            'expectedResult' => 'String with string   and to few arguments',
+        ];
+        yield 'String with string %s %s and to many arguments' => [
+            'detailString' => 'String with string %s %s and to many arguments',
+            'substitutes' => [0 => 'astring', 1 => 'another string', 2 => 'a third string'],
+            'expectedResult' => 'String with string astring another string and to many arguments',
+        ];
+    }
+
+    #[DataProvider('formatLogDetailsLegacyStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsStaticLegacyStrings(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, LogDataTraitTestAccessor::callFormatLogDetailsStatic($detailString, $substitutes));
+    }
+
+    #[DataProvider('formatLogDetailsLegacyStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsLegacyStrings(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, $substitutes));
+    }
+
+    #[DataProvider('formatLogDetailsLegacyStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsLegacyStringsWithSerializedSubstitutes(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, serialize($substitutes)));
+    }
+
+    #[DataProvider('formatLogDetailsLegacyStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsLegacyStringsWithJsonEncodedSubstitutes(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, json_encode($substitutes)));
+    }
+
+    public static function formatLogDetailsNewFormatStringsDataProvider(): \Generator
+    {
+        yield 'Empty arguments {aPlaceholder}' => [
+            'detailString' => 'Empty arguments {aPlaceholder}',
+            'substitutes' => [],
+            'expectedResult' => 'Empty arguments {aPlaceholder}',
+        ];
+        yield 'Non existing arguments {aPlaceholder}' => [
+            'detailString' => 'Non existing arguments {aPlaceholder}',
+            'substitutes' => ['non-existing-argument' => 'non-existing-argument'],
+            'expectedResult' => 'Non existing arguments {aPlaceholder}',
+        ];
+        yield 'Single argument {myPlaceholder}' => [
+            'detailString' => 'Single argument {myPlaceholder}',
+            'substitutes' => ['myPlaceholder' => 'replacedPlacerHolder'],
+            'expectedResult' => 'Single argument replacedPlacerHolder',
+        ];
+        yield 'Multiple argument {myPlaceholder} {anotherPlaceholder}' => [
+            'detailString' => 'Multiple argument {myPlaceholder} {anotherPlaceholder}',
+            'substitutes' => ['myPlaceholder' => 'replacedPlacerHolder', 'anotherPlaceholder' => 'replacedAnotherPlaceholder'],
+            'expectedResult' => 'Multiple argument replacedPlacerHolder replacedAnotherPlaceholder',
+        ];
+        yield 'Multiple argument {myPlaceholder} {anotherPlaceholder} to many arguments' => [
+            'detailString' => 'Multiple argument {myPlaceholder} {anotherPlaceholder} to many arguments',
+            'substitutes' => ['non-existing-argument' => 'non-existing-argument', 'myPlaceholder' => 'replacedPlacerHolder', 'anotherPlaceholder' => 'replacedAnotherPlaceholder'],
+            'expectedResult' => 'Multiple argument replacedPlacerHolder replacedAnotherPlaceholder to many arguments',
+        ];
+    }
+
+    #[DataProvider('formatLogDetailsNewFormatStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsStaticNewFormatStrings(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, LogDataTraitTestAccessor::callFormatLogDetailsStatic($detailString, $substitutes));
+    }
+
+    #[DataProvider('formatLogDetailsNewFormatStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsNewFormatStrings(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, $substitutes));
+    }
+
+    #[DataProvider('formatLogDetailsNewFormatStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsNewFormatStringsWithSerializedSubstitutes(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, serialize($substitutes)));
+    }
+
+    #[DataProvider('formatLogDetailsNewFormatStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsNewFormatStringsWithJsonEncodedSubstitutes(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, json_encode($substitutes)));
+    }
+
+    public static function formatLogDetailsMixedFormatStringsDataProvider(): \Generator
+    {
+        yield 'Mixed empty arguments % %s {aPlaceholder}' => [
+            'detailString' => 'Mixed empty arguments % %s {aPlaceholder}',
+            'substitutes' => [],
+            'expectedResult' => 'Mixed empty arguments %  {aPlaceholder}',
+        ];
+        yield 'Mixed non existing arguments % %s {aPlaceholder}' => [
+            'detailString' => 'Mixed non existing arguments % %s {aPlaceholder}',
+            'substitutes' => ['non-existing-argument' => 'non-existing-argument'],
+            'expectedResult' => 'Mixed non existing arguments  {aPlaceholder}',
+        ];
+        yield 'Mixed Single argument % %s {myPlaceholder}' => [
+            'detailString' => 'Mixed Single argument % %s {myPlaceholder}',
+            'substitutes' => ['myPlaceholder' => 'replacedPlacerHolder'],
+            'expectedResult' => 'Mixed Single argument  replacedPlacerHolder',
+        ];
+        yield 'Mixed multiple argument % %s {myPlaceholder} {anotherPlaceholder}' => [
+            'detailString' => 'Mixed multiple argument % %s {myPlaceholder} {anotherPlaceholder}',
+            'substitutes' => ['myPlaceholder' => 'replacedPlacerHolder', 'anotherPlaceholder' => 'replacedAnotherPlaceholder'],
+            'expectedResult' => 'Mixed multiple argument  replacedPlacerHolder replacedAnotherPlaceholder',
+        ];
+        yield 'Mixed multiple argument % %s {myPlaceholder} {anotherPlaceholder} to many arguments' => [
+            'detailString' => 'Mixed multiple argument % %s {myPlaceholder} {anotherPlaceholder} to many arguments',
+            'substitutes' => ['non-existing-argument' => 'non-existing-argument', 'myPlaceholder' => 'replacedPlacerHolder', 'anotherPlaceholder' => 'replacedAnotherPlaceholder'],
+            'expectedResult' => 'Mixed multiple argument  replacedPlacerHolder replacedAnotherPlaceholder to many arguments',
+        ];
+    }
+
+    #[DataProvider('formatLogDetailsMixedFormatStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsStaticMixedFormatStrings(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, LogDataTraitTestAccessor::callFormatLogDetailsStatic($detailString, $substitutes));
+    }
+
+    #[DataProvider('formatLogDetailsMixedFormatStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsMixedFormatStrings(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, $substitutes));
+    }
+
+    #[DataProvider('formatLogDetailsMixedFormatStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsMixedFormatStringsWithSerializedSubstitutes(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, serialize($substitutes)));
+    }
+
+    #[DataProvider('formatLogDetailsMixedFormatStringsDataProvider')]
+    #[Test]
+    public function formatLogDetailsMixedFormatStringsWithJsonEncodedSubstitutes(string $detailString, array $substitutes, string $expectedResult): void
+    {
+        self::assertSame($expectedResult, (new LogDataTraitTestAccessor())->callFormatLogDetails($detailString, json_encode($substitutes)));
+    }
+}