diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/TanPairingException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/TanPairingException.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ae4de503cf0c38f7b842b4485e1e5784934a1a01
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/TanPairingException.kt
@@ -0,0 +1,24 @@
+package de.rki.coronawarnapp.exception
+
+import android.content.Context
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.exception.http.CwaClientError
+import de.rki.coronawarnapp.util.HasHumanReadableError
+import de.rki.coronawarnapp.util.HumanReadableError
+
+/**
+ * Specific Exception type to identify an error case happening when TAN is retrieved.
+ * @see <a href="https://jira-ibs.wbs.net.sap/browse/EXPOSUREAPP-4515">EXPOSUREAPP-4515</a>
+ */
+class TanPairingException(
+    override val code: Int,
+    override val message: String?,
+    override val cause: Throwable?
+) : CwaClientError(code, message, cause), HasHumanReadableError {
+    override fun toHumanReadableError(context: Context): HumanReadableError {
+        return HumanReadableError(
+            title = context.getString(R.string.submission_error_dialog_web_paring_invalid_title),
+            description = context.getString(R.string.submission_error_dialog_web_paring_invalid_body)
+        )
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/HttpModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/HttpModule.kt
index a921a828fdc1e36ebdeb51188ed4553e94a64cfd..438e7095274ac7edcb22d4e2febc4824fa424b31 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/HttpModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/HttpModule.kt
@@ -28,11 +28,7 @@ class HttpModule {
     fun defaultHttpClient(): OkHttpClient {
         val interceptors: List<Interceptor> = listOf(
             WebSecurityVerificationInterceptor(),
-            HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
-                override fun log(message: String) {
-                    Timber.tag("OkHttp").v(message)
-                }
-            }).apply {
+            HttpLoggingInterceptor { message -> Timber.tag("OkHttp").v(message) }.apply {
                 if (BuildConfig.DEBUG) setLevel(HttpLoggingInterceptor.Level.BODY)
             },
             RetryInterceptor(),
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/playbook/DefaultPlaybook.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/playbook/DefaultPlaybook.kt
index 7d981a1c81f08e314fe4456bcce87448dbf686a9..6988043fd5c36d227973eed3d8f2237c55d4a0b0 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/playbook/DefaultPlaybook.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/playbook/DefaultPlaybook.kt
@@ -1,5 +1,7 @@
 package de.rki.coronawarnapp.playbook
 
+import de.rki.coronawarnapp.exception.TanPairingException
+import de.rki.coronawarnapp.exception.http.BadRequestException
 import de.rki.coronawarnapp.submission.server.SubmissionServer
 import de.rki.coronawarnapp.util.formatter.TestResult
 import de.rki.coronawarnapp.verification.server.VerificationKeyType
@@ -90,23 +92,46 @@ class DefaultPlaybook @Inject constructor(
         // fake verification
         ignoreExceptions { verificationServer.retrieveTanFake() }
 
-        // real submission
-        if (authCode != null) {
-            val serverSubmissionData = SubmissionServer.SubmissionData(
-                authCode = authCode,
-                keyList = data.temporaryExposureKeys,
-                consentToFederation = data.consentToFederation,
-                visistedCountries = data.visistedCountries
+        // submitKeysToServer could throw BadRequestException too.
+        try {
+            // real submission
+            if (authCode != null) {
+                val serverSubmissionData = SubmissionServer.SubmissionData(
+                    authCode = authCode,
+                    keyList = data.temporaryExposureKeys,
+                    consentToFederation = data.consentToFederation,
+                    visistedCountries = data.visistedCountries
+                )
+                submissionServer.submitKeysToServer(serverSubmissionData)
+                coroutineScope.launch { followUpPlaybooks() }
+            } else {
+                submissionServer.submitKeysToServerFake()
+                coroutineScope.launch { followUpPlaybooks() }
+                propagateException(wrapException(exception))
+            }
+        } catch (exception: BadRequestException) {
+            propagateException(
+                TanPairingException(
+                    code = exception.statusCode,
+                    message = "Invalid payload or missing header",
+                    cause = exception
+                )
             )
-            submissionServer.submitKeysToServer(serverSubmissionData)
-            coroutineScope.launch { followUpPlaybooks() }
-        } else {
-            submissionServer.submitKeysToServerFake()
-            coroutineScope.launch { followUpPlaybooks() }
-            propagateException(exception)
         }
     }
 
+    /**
+     * Distinguish BadRequestException to present more insightful message to the end user
+     */
+    private fun wrapException(exception: Exception?) = when (exception) {
+        is BadRequestException -> TanPairingException(
+            code = exception.statusCode,
+            message = "Tan has been retrieved before for this registration token",
+            cause = exception
+        )
+        else -> exception
+    }
+
     private suspend fun dummy(launchFollowUp: Boolean) {
         // fake verification
         ignoreExceptions { verificationServer.retrieveTanFake() }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
index b22bf0f54c88e09130bd24ee19c2d84bdba25f3a..24e670aff5571534d7c60025bda5e30b05fb36b8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
@@ -421,7 +421,7 @@ object LocalData {
             )
         }
 
-    fun devicePairingSuccessfulTimestamp(): Long? {
+    fun devicePairingSuccessfulTimestamp(): Long {
         return getSharedPreferenceInstance().getLong(
             CoronaWarnApplication.getAppContext()
                 .getString(R.string.preference_device_pairing_successful_time),
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/http/playbook/DefaultPlaybookTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/http/playbook/DefaultPlaybookTest.kt
index 7a710623431ea78e0b7e1d380754985a0145c980..7f04b80939b8d8953c3ea1601ff9e40b7b0361a0 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/http/playbook/DefaultPlaybookTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/http/playbook/DefaultPlaybookTest.kt
@@ -1,5 +1,7 @@
 package de.rki.coronawarnapp.http.playbook
 
+import de.rki.coronawarnapp.exception.TanPairingException
+import de.rki.coronawarnapp.exception.http.BadRequestException
 import de.rki.coronawarnapp.playbook.DefaultPlaybook
 import de.rki.coronawarnapp.playbook.Playbook
 import de.rki.coronawarnapp.submission.server.SubmissionServer
@@ -8,6 +10,7 @@ import de.rki.coronawarnapp.verification.server.VerificationKeyType
 import de.rki.coronawarnapp.verification.server.VerificationServer
 import io.kotest.assertions.throwables.shouldThrow
 import io.kotest.matchers.shouldBe
+import io.kotest.matchers.types.shouldBeInstanceOf
 import io.mockk.MockKAnnotations
 import io.mockk.clearAllMocks
 import io.mockk.coEvery
@@ -102,6 +105,44 @@ class DefaultPlaybookTest : BaseTest() {
         }
     }
 
+    @Test
+    fun `tan retrieval throws human readable exception`(): Unit = runBlocking {
+        coEvery { verificationServer.retrieveTan(any()) } throws BadRequestException(null)
+        try {
+            createPlaybook().submit(
+                Playbook.SubmissionData(
+                    registrationToken = "token",
+                    temporaryExposureKeys = listOf(),
+                    consentToFederation = true,
+                    visistedCountries = listOf("DE")
+                )
+            )
+        } catch (e: Exception) {
+            e.shouldBeInstanceOf<TanPairingException>()
+            e.cause.shouldBeInstanceOf<BadRequestException>()
+            e.message shouldBe "Tan has been retrieved before for this registration token"
+        }
+    }
+
+    @Test
+    fun `keys submission throws human readable exception`(): Unit = runBlocking {
+        coEvery { submissionServer.submitKeysToServer(any()) } throws BadRequestException(null)
+        try {
+            createPlaybook().submit(
+                Playbook.SubmissionData(
+                    registrationToken = "token",
+                    temporaryExposureKeys = listOf(),
+                    consentToFederation = true,
+                    visistedCountries = listOf("DE")
+                )
+            )
+        } catch (e: Exception) {
+            e.shouldBeInstanceOf<TanPairingException>()
+            e.cause.shouldBeInstanceOf<BadRequestException>()
+            e.message shouldBe "Invalid payload or missing header"
+        }
+    }
+
     @Test
     fun `submission matches request pattern despite missing authcode`(): Unit = runBlocking {
         coEvery { verificationServer.retrieveTan(any()) } throws TestException()