StackOverflowError on App start
<!--
    Thanks for your interest in using the App Center SDK for Android.
    If your issue is not related to using our Android SDK but rather about the product experience like the portal or CI,
    please create a ticket using the blue chat button on any page of the https://appcenter.ms portal instead.
    If you are using Xamarin, please report the issue on https://github.com/Microsoft/AppCenter-SDK-DotNet instead. 
-->

### **Description**

StackOverflowError during app start

<!-- If making a feature request, remove the below information -->

### **Repro Steps**

Please list the steps used to reproduce your issue.

1. connect app to app center with provided code
2. after using you app for a while, when it crashes, bad things happen

### **Details**

1. Which SDK version are you using?
    - 1.8.0
2. Which OS version did you experience the issue on?
    - Android 7.0
3. What device version did you see this error on?  Were you using an emulator or a physical device?
    - physical tablet device
4. What third party libraries are you using?
    - lots of them, but I lack to see the importance here

5. Please enable verbose logging for your app using `AppCenter.setLogLevel(Log.VERBOSE)` before your call to `AppCenter.start(...)` and include the logs here:

I will work on collecting the logs, as I need a new deploy, but for now, here is the stack trace:
```
STACK_TRACE: java.lang.StackOverflowError
	at dalvik.system.BlockGuard.getThreadPolicy(BlockGuard.java:140)
	at libcore.io.BlockGuardOs.read(BlockGuardOs.java:148)
	at libcore.io.IoBridge.read(IoBridge.java:432)
	at java.io.FileInputStream.read(FileInputStream.java:179)
	at libcore.io.Streams.readSingleByte(Streams.java:41)
	at java.io.FileInputStream.read(FileInputStream.java:175)
	at java.io.FilterInputStream.read(FilterInputStream.java:114)
	at java.io.ObjectInputStream.checkReadPrimitiveTypes(ObjectInputStream.java:405)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:770)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at java.io.ObjectInputStream.readFieldValues(ObjectInputStream.java:1137)
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:455)
	at java.lang.Throwable.readObject(Throwable.java:444)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1354)
	at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1266)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1855)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1960)
	at com.microsoft.appcenter.utils.storage.StorageHelper$InternalStorage.readObject(StorageHelper.java:425)
	at com.microsoft.appcenter.crashes.Crashes.buildErrorReport(Crashes.java:750)
	at com.microsoft.appcenter.crashes.Crashes.processPendingErrors(Crashes.java:646)
	at com.microsoft.appcenter.crashes.Crashes.onStarted(Crashes.java:367)
	at com.microsoft.appcenter.AppCenter.finishStartServices(AppCenter.java:787)
	at com.microsoft.appcenter.AppCenter.access$500(AppCenter.java:49)
	at com.microsoft.appcenter.AppCenter$8.run(AppCenter.java:709)
	at android.os.Handler.handleCallback(Handler.java:615)
	at android.os.Handler.dispatchMessage(Handler.java:92)
	at android.os.Looper.loop(Looper.java:137)
	at android.os.HandlerThread.run(HandlerThread.java:60)
```

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad200b285..c8b37a4a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # App Center SDK for Android Change Log
 
+## Version 1.10.0 (Under active development)
+
+### AppCenterCrashes
+
+* **[Fix]** Preventing stack overflow crash while reading a huge throwable file.
+
+___
+
 ## Version 1.9.0
 
 ### AppCenter
@@ -14,6 +22,8 @@
 * **[Feature]** Add support for typed properties. Note that these APIs still convert properties back to strings on the App Center backend. More work is needed to store and display typed properties in the App Center portal. Using the new APIs now will enable future scenarios, but for now the behavior will be the same as it is for current event properties.
 * **[Feature]** Preparation work for a future change in transmission protocol and endpoint for Analytics data. There is no impact on your current workflow when using App Center.
 
+___
+
 ## Version 1.8.0
 
 ### AppCenterCrashes
diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java
index c2fab2ebd..5858e8f8d 100644
--- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java
+++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java
@@ -63,6 +63,17 @@ public class CrashActivity extends AppCompatActivity {
                     run();
                 }
             }),
+            new Crash(R.string.title_deeply_nested_exception_crash, R.string.description_deeply_nested_exception_crash, new Runnable() {
+
+                @Override
+                public void run() {
+                    Exception e = new Exception();
+                    for (int i = 0; i < 1000; i++) {
+                        e = new Exception(String.valueOf(i), e);
+                    }
+                    throw new RuntimeException(e);
+                }
+            }),
             new Crash(R.string.title_memory_crash, R.string.description_memory_crash, new Runnable() {
 
                 @Override
diff --git a/apps/sasquatch/src/main/res/values/crashes.xml b/apps/sasquatch/src/main/res/values/crashes.xml
index 7290760c7..1b9f7508d 100644
--- a/apps/sasquatch/src/main/res/values/crashes.xml
+++ b/apps/sasquatch/src/main/res/values/crashes.xml
@@ -6,6 +6,8 @@
     <string name="description_crash_divide_by_0" tools:ignore="MissingTranslation">Divide by 0</string>
     <string name="title_test_ui_crash" tools:ignore="MissingTranslation">Test some UI crash</string>
     <string name="description_test_ui_crash" tools:ignore="MissingTranslation">A UI coding mistake, has inner exception</string>
+    <string name="title_deeply_nested_exception_crash" tools:ignore="MissingTranslation">A exception with plenty of causes</string>
+    <string name="description_deeply_nested_exception_crash" tools:ignore="MissingTranslation">The exception that caused the exception that caused the exception that caused the exception…</string>
     <string name="title_stack_overflow_crash" tools:ignore="MissingTranslation">Stack overflow crash</string>
     <string name="description_stack_overflow_crash" tools:ignore="MissingTranslation">Caused by infinite recursion</string>
     <string name="title_memory_crash" tools:ignore="MissingTranslation">Out of memory crash</string>
diff --git a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java
index f88a4a07d..84cb26f0c 100644
--- a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java
+++ b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java
@@ -16,6 +16,7 @@ import com.microsoft.appcenter.crashes.utils.ErrorLogHelper;
 import com.microsoft.appcenter.ingestion.Ingestion;
 import com.microsoft.appcenter.ingestion.models.Log;
 import com.microsoft.appcenter.utils.HandlerUtils;
+import com.microsoft.appcenter.utils.UUIDUtils;
 import com.microsoft.appcenter.utils.async.AppCenterConsumer;
 import com.microsoft.appcenter.utils.storage.StorageHelper;
 
@@ -30,6 +31,7 @@ import org.mockito.stubbing.Answer;
 
 import java.io.File;
 import java.io.FileFilter;
+import java.io.FileWriter;
 import java.util.Collections;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicReference;
@@ -129,7 +131,41 @@ public class CrashesAndroidTest {
     }
 
     @Test
-    public void getLastSessionCrashReport() throws Exception {
+    public void getLastSessionCrashReportSimpleException() throws Exception {
+
+        /* Null before start. */
+        Crashes.unsetInstance();
+        assertNull(Crashes.getLastSessionCrashReport().get());
+        assertFalse(Crashes.hasCrashedInLastSession().get());
+
+        /* Crash on 1st process. */
+        Thread.UncaughtExceptionHandler uncaughtExceptionHandler = mock(Thread.UncaughtExceptionHandler.class);
+        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
+        startFresh(null);
+        assertNull(Crashes.getLastSessionCrashReport().get());
+        assertFalse(Crashes.hasCrashedInLastSession().get());
+        final RuntimeException exception = new IllegalArgumentException();
+        final Thread thread = new Thread() {
+
+            @Override
+            public void run() {
+                throw exception;
+            }
+        };
+        thread.start();
+        thread.join();
+
+        /* Get last session crash on 2nd process. */
+        startFresh(null);
+        ErrorReport errorReport = Crashes.getLastSessionCrashReport().get();
+        assertNotNull(errorReport);
+        Throwable lastThrowable = errorReport.getThrowable();
+        assertTrue(lastThrowable instanceof IllegalArgumentException);
+        assertTrue(Crashes.hasCrashedInLastSession().get());
+    }
+
+    @Test
+    public void getLastSessionCrashReportStackOverflowException() throws Exception {
 
         /* Null before start. */
         Crashes.unsetInstance();
@@ -158,9 +194,62 @@ public class CrashesAndroidTest {
         startFresh(null);
         ErrorReport errorReport = Crashes.getLastSessionCrashReport().get();
         assertNotNull(errorReport);
+        Throwable lastThrowable = errorReport.getThrowable();
+        assertTrue(lastThrowable instanceof StackOverflowError);
+        assertEquals(ErrorLogHelper.FRAME_LIMIT, lastThrowable.getStackTrace().length);
         assertTrue(Crashes.hasCrashedInLastSession().get());
     }
 
+    @Test
+    public void getLastSessionCrashReportExceptionWithHugeFramesAndHugeCauses() throws Exception {
+
+        /* Null before start. */
+        Crashes.unsetInstance();
+        assertNull(Crashes.getLastSessionCrashReport().get());
+        assertFalse(Crashes.hasCrashedInLastSession().get());
+
+        /* Crash on 1st process. */
+        Thread.UncaughtExceptionHandler uncaughtExceptionHandler = mock(Thread.UncaughtExceptionHandler.class);
+        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
+        startFresh(null);
+        assertNull(Crashes.getLastSessionCrashReport().get());
+        assertFalse(Crashes.hasCrashedInLastSession().get());
+        final RuntimeException exception = generateHugeException(300, 300);
+        assertTrue(exception.getStackTrace().length > ErrorLogHelper.FRAME_LIMIT);
+        final Thread thread = new Thread() {
+
+            @Override
+            public void run() {
+                throw exception;
+            }
+        };
+        thread.start();
+        thread.join();
+
+        /* Get last session crash on 2nd process. */
+        startFresh(null);
+        ErrorReport errorReport = Crashes.getLastSessionCrashReport().get();
+        assertNotNull(errorReport);
+
+        /* The client side throwable failed to save as huge so will be null. */
+        assertNull(errorReport.getThrowable());
+        assertTrue(Crashes.hasCrashedInLastSession().get());
+
+        /* Check managed error was sent as truncated. */
+        ArgumentCaptor<ManagedErrorLog> errorLog = ArgumentCaptor.forClass(ManagedErrorLog.class);
+        verify(mChannel).enqueue(errorLog.capture(), eq(Crashes.ERROR_GROUP));
+        assertNotNull(errorLog.getValue());
+        assertNotNull(errorLog.getValue().getException());
+        assertNotNull(errorLog.getValue().getException().getFrames());
+        assertEquals(ErrorLogHelper.FRAME_LIMIT, errorLog.getValue().getException().getFrames().size());
+        int causesCount = 0;
+        com.microsoft.appcenter.crashes.ingestion.models.Exception e = errorLog.getValue().getException();
+        while (e.getInnerExceptions() != null && (e = e.getInnerExceptions().get(0)) != null) {
+            causesCount++;
+        }
+        assertEquals(ErrorLogHelper.CAUSE_LIMIT, causesCount + 1);
+    }
+
     @Test
     public void getLastSessionCrashReportNative() throws Exception {
 
@@ -219,10 +308,25 @@ public class CrashesAndroidTest {
         }
     }
 
+    @Test
+    public void clearInvalidFiles() throws Exception {
+        File invalidFile1 = new File(ErrorLogHelper.getErrorStorageDirectory(), UUIDUtils.randomUUID() + ErrorLogHelper.ERROR_LOG_FILE_EXTENSION);
+        File invalidFile2 = new File(ErrorLogHelper.getErrorStorageDirectory(), UUIDUtils.randomUUID() + ErrorLogHelper.ERROR_LOG_FILE_EXTENSION);
+        assertTrue(invalidFile1.createNewFile());
+        new FileWriter(invalidFile2).append("fake_data").close();
+        assertEquals(2, ErrorLogHelper.getStoredErrorLogFiles().length);
+
+        /* Invalid files should be cleared. */
+        startFresh(null);
+        assertTrue(Crashes.isEnabled().get());
+        assertEquals(0, ErrorLogHelper.getStoredErrorLogFiles().length);
+    }
+
     @Test
     public void testNoDuplicateCallbacksOrSending() throws Exception {
 
         /* Crash on 1st process. */
+        Crashes.unsetInstance();
         assertFalse(Crashes.hasCrashedInLastSession().get());
         android.util.Log.i(TAG, "Process 1");
         Thread.UncaughtExceptionHandler uncaughtExceptionHandler = mock(Thread.UncaughtExceptionHandler.class);
@@ -240,8 +344,7 @@ public class CrashesAndroidTest {
         });
         when(crashesListener.shouldAwaitUserConfirmation()).thenReturn(true);
         startFresh(crashesListener);
-        final Error exception = generateStackOverflowError();
-        assertTrue(exception.getStackTrace().length > ErrorLogHelper.FRAME_LIMIT);
+        final RuntimeException exception = new RuntimeException();
         final Thread thread = new Thread() {
 
             @Override
@@ -251,7 +354,6 @@ public class CrashesAndroidTest {
         };
         thread.start();
         thread.join();
-        assertEquals(ErrorLogHelper.FRAME_LIMIT, exception.getStackTrace().length);
         verify(uncaughtExceptionHandler).uncaughtException(thread, exception);
         assertEquals(2, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length);
         verifyZeroInteractions(crashesListener);
@@ -267,8 +369,7 @@ public class CrashesAndroidTest {
             public void accept(ErrorReport errorReport) {
                 assertNotNull(errorReport);
                 Throwable lastThrowable = errorReport.getThrowable();
-                assertTrue(lastThrowable instanceof StackOverflowError);
-                assertEquals(ErrorLogHelper.FRAME_LIMIT, lastThrowable.getStackTrace().length);
+                assertTrue(lastThrowable instanceof RuntimeException);
             }
         });
         assertTrue(Crashes.hasCrashedInLastSession().get());
@@ -351,13 +452,6 @@ public class CrashesAndroidTest {
         verify(crashesListener).onBeforeSending(any(ErrorReport.class));
         verify(crashesListener).onSendingSucceeded(any(ErrorReport.class));
         verifyNoMoreInteractions(crashesListener);
-
-        /* Verify log was truncated to 256 frames. */
-        assertTrue(log.get() instanceof ManagedErrorLog);
-        ManagedErrorLog errorLog = (ManagedErrorLog) log.get();
-        assertNotNull(errorLog.getException());
-        assertNotNull(errorLog.getException().getFrames());
-        assertEquals(ErrorLogHelper.FRAME_LIMIT, errorLog.getException().getFrames().size());
     }
 
     @Test
@@ -493,11 +587,26 @@ public class CrashesAndroidTest {
         assertEquals(2, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length);
     }
 
-    private Error generateStackOverflowError() {
+    private static Error generateStackOverflowError() {
         try {
             return generateStackOverflowError();
         } catch (StackOverflowError error) {
             return error;
         }
     }
+
+    @SuppressWarnings("SameParameterValue")
+    private static RuntimeException generateHugeException(int stacktraceIncrease, int causes) {
+        if (stacktraceIncrease > 0) {
+            try {
+                return generateHugeException(stacktraceIncrease - 1, causes);
+            } catch (StackOverflowError ignore) {
+            }
+        }
+        Exception e = new Exception();
+        for (int i = 0; i < causes; i++) {
+            e = new Exception(Integer.valueOf(i).toString(), e);
+        }
+        return new RuntimeException(e);
+    }
 }
diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java
index 197d3698c..31ebbd3eb 100644
--- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java
+++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java
@@ -617,6 +617,13 @@ public class Crashes extends AbstractAppCenterService {
 
             /* Check last session crash. */
             File logFile = ErrorLogHelper.getLastErrorLogFile();
+            while (logFile != null && logFile.length() == 0) {
+                AppCenterLog.warn(Crashes.LOG_TAG, "Deleting empty error file: " + logFile);
+
+                //noinspection ResultOfMethodCallIgnored
+                logFile.delete();
+                logFile = ErrorLogHelper.getLastErrorLogFile();
+            }
             if (logFile != null) {
                 AppCenterLog.debug(LOG_TAG, "Processing crash report for the last session.");
                 String logFileContents = StorageHelper.InternalStorage.read(logFile);
@@ -656,7 +663,10 @@ public class Crashes extends AbstractAppCenterService {
                         removeAllStoredErrorLogFiles(id);
                     }
                 } catch (JSONException e) {
-                    AppCenterLog.error(LOG_TAG, "Error parsing error log", e);
+                    AppCenterLog.error(LOG_TAG, "Error parsing error log. Deleting invalid file: " + logFile, e);
+
+                    //noinspection ResultOfMethodCallIgnored
+                    logFile.delete();
                 }
             }
         }
@@ -744,19 +754,17 @@ public class Crashes extends AbstractAppCenterService {
         } else {
             File file = ErrorLogHelper.getStoredThrowableFile(id);
             if (file != null) {
-                try {
-                    Throwable throwable = null;
-                    if (file.length() > 0) {
+                Throwable throwable = null;
+                if (file.length() > 0) {
+                    try {
                         throwable = StorageHelper.InternalStorage.readObject(file);
+                    } catch (IOException | ClassNotFoundException | StackOverflowError e) {
+                        AppCenterLog.error(LOG_TAG, "Cannot read throwable file " + file.getName(), e);
                     }
-                    ErrorReport report = ErrorLogHelper.getErrorReportFromErrorLog(log, throwable);
-                    mErrorReportCache.put(id, new ErrorLogReport(log, report));
-                    return report;
-                } catch (ClassNotFoundException ignored) {
-                    AppCenterLog.error(LOG_TAG, "Cannot read throwable file " + file.getName(), ignored);
-                } catch (IOException ignored) {
-                    AppCenterLog.error(LOG_TAG, "Cannot access serialized throwable file " + file.getName(), ignored);
                 }
+                ErrorReport report = ErrorLogHelper.getErrorReportFromErrorLog(log, throwable);
+                mErrorReportCache.put(id, new ErrorLogReport(log, report));
+                return report;
             }
         }
         return null;
@@ -936,9 +944,18 @@ public class Crashes extends AbstractAppCenterService {
         AppCenterLog.debug(Crashes.LOG_TAG, "Saved JSON content for ingestion into " + errorLogFile);
         File throwableFile = new File(errorStorageDirectory, filename + ErrorLogHelper.THROWABLE_FILE_EXTENSION);
         if (throwable != null) {
-            StorageHelper.InternalStorage.writeObject(throwableFile, throwable);
-            AppCenterLog.debug(Crashes.LOG_TAG, "Saved Throwable as is for client side inspection in " + throwableFile + " throwable:", throwable);
-        } else {
+            try {
+                StorageHelper.InternalStorage.writeObject(throwableFile, throwable);
+                AppCenterLog.debug(Crashes.LOG_TAG, "Saved Throwable as is for client side inspection in " + throwableFile + " throwable:", throwable);
+            } catch (StackOverflowError e) {
+                AppCenterLog.error(Crashes.LOG_TAG, "Failed to store throwable", e);
+                throwable = null;
+
+                //noinspection ResultOfMethodCallIgnored
+                throwableFile.delete();
+            }
+        }
+        if (throwable == null) {
 
             /*
              * If there is no Java Throwable to save as is (typical in wrapper SDKs),
diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java
index 9ccb84af9..67ce1f26e 100644
--- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java
+++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -68,15 +69,26 @@ public class ErrorLogHelper {
     public static final int FRAME_LIMIT = 256;
 
     /**
-     * Error log directory within application files.
+     * We keep the first half of the limit of frames from the beginning and the second half from end.
+     */
+    private static final int FRAME_LIMIT_HALF = FRAME_LIMIT / 2;
+
+    /**
+     * For huge exception cause chains, we keep only beginning and end of causes according to this limit.
      */
     @VisibleForTesting
-    static final String ERROR_DIRECTORY = "error";
+    public static final int CAUSE_LIMIT = 16;
 
     /**
-     * We keep the first half of the limit of frames from the beginning and the second half from end.
+     * We keep the first half of the limit of causes from the beginning and the second half from end.
      */
-    private static final int FRAME_LIMIT_HALF = FRAME_LIMIT / 2;
+    private static final int CAUSE_LIMIT_HALF = CAUSE_LIMIT / 2;
+
+    /**
+     * Error log directory within application files.
+     */
+    @VisibleForTesting
+    static final String ERROR_DIRECTORY = "error";
 
     /**
      * Root directory for error log and throwable files.
@@ -303,7 +315,15 @@ public class ErrorLogHelper {
     public static Exception getModelExceptionFromThrowable(@NonNull Throwable t) {
         Exception topException = null;
         Exception parentException = null;
+        List<Throwable> causeChain = new LinkedList<>();
         for (Throwable cause = t; cause != null; cause = cause.getCause()) {
+            causeChain.add(cause);
+        }
+        if (causeChain.size() > CAUSE_LIMIT) {
+            AppCenterLog.warn(Crashes.LOG_TAG, "Crash causes truncated from " + causeChain.size() + " to " + CAUSE_LIMIT + " causes.");
+            causeChain.subList(CAUSE_LIMIT_HALF, causeChain.size() - CAUSE_LIMIT_HALF).clear();
+        }
+        for (Throwable cause: causeChain) {
             Exception exception = new Exception();
             exception.setType(cause.getClass().getName());
             exception.setMessage(cause.getMessage());
@@ -315,6 +335,8 @@ public class ErrorLogHelper {
             }
             parentException = exception;
         }
+
+        //noinspection ConstantConditions
         return topException;
     }
 
diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java
index f903a0a7f..dbe7da8c6 100644
--- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java
+++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java
@@ -702,7 +702,9 @@ public class CrashesTest {
         ErrorReport errorReport = ErrorLogHelper.getErrorReportFromErrorLog(mErrorLog, EXCEPTION);
 
         mockStatic(ErrorLogHelper.class);
-        when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(mock(File.class));
+        File errorLogFile = mock(File.class);
+        when(errorLogFile.length()).thenReturn(1L);
+        when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(errorLogFile);
         when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{mock(File.class)});
         when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]);
         File throwableFile = mock(File.class);
@@ -885,22 +887,37 @@ public class CrashesTest {
         File throwableFile = mock(File.class);
         when(throwableFile.length()).thenReturn(1L);
         when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(throwableFile);
-
-        Exception classNotFoundException = mock(ClassNotFoundException.class);
-        Exception ioException = mock(IOException.class);
-        when(StorageHelper.InternalStorage.readObject(any(File.class))).thenThrow(classNotFoundException).thenThrow(ioException);
-
+        when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(mock(ErrorReport.class));
+
+        /* Mock exceptions. */
+        Throwable classNotFoundException = mock(ClassNotFoundException.class);
+        Throwable ioException = mock(IOException.class);
+        Throwable stackOverflowError = mock(StackOverflowError.class);
+        when(StorageHelper.InternalStorage.readObject(any(File.class)))
+                .thenThrow(classNotFoundException)
+                .thenThrow(ioException)
+                .thenThrow(stackOverflowError);
         Crashes crashes = Crashes.getInstance();
 
+        /* Test ClassNotFoundException. */
         ErrorReport report = crashes.buildErrorReport(mErrorLog);
-        assertNull(report);
-        report = crashes.buildErrorReport(mErrorLog);
-        assertNull(report);
-
+        assertNotNull(report);
         verifyStatic();
         AppCenterLog.error(eq(Crashes.LOG_TAG), anyString(), eq(classNotFoundException));
+
+        /* Test IOException. */
+        mErrorLog.setId(UUIDUtils.randomUUID());
+        report = crashes.buildErrorReport(mErrorLog);
+        assertNotNull(report);
         verifyStatic();
         AppCenterLog.error(eq(Crashes.LOG_TAG), anyString(), eq(ioException));
+
+        /* Test StackOverflowError. */
+        mErrorLog.setId(UUIDUtils.randomUUID());
+        report = crashes.buildErrorReport(mErrorLog);
+        assertNotNull(report);
+        verifyStatic();
+        AppCenterLog.error(eq(Crashes.LOG_TAG), anyString(), eq(stackOverflowError));
     }
 
     @Test
@@ -987,6 +1004,7 @@ public class CrashesTest {
 
         mockStatic(ErrorLogHelper.class);
         File lastErrorLogFile = errorStorageDirectory.newFile("last-error-log.json");
+        new FileWriter(lastErrorLogFile).append("fake_data").close();
         when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(lastErrorLogFile);
         File throwableFile = errorStorageDirectory.newFile();
         new FileWriter(throwableFile).append("fake_data").close();
@@ -994,7 +1012,7 @@ public class CrashesTest {
         when(ErrorLogHelper.getErrorReportFromErrorLog(errorLog, throwable)).thenReturn(errorReport);
         when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{lastErrorLogFile});
         when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]);
-        when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn("");
+        when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn("fake_data");
         when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(throwable);
 
         Crashes crashes = Crashes.getInstance();
@@ -1059,10 +1077,11 @@ public class CrashesTest {
 
         mockStatic(ErrorLogHelper.class);
         File lastErrorLogFile = errorStorageDirectory.newFile("last-error-log.json");
+        new FileWriter(lastErrorLogFile).append("fake_data").close();
         when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(lastErrorLogFile);
         when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{lastErrorLogFile});
         when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]);
-        when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn("");
+        when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn("fake_data");
 
         Crashes crashes = Crashes.getInstance();
         crashes.setLogSerializer(logSerializer);
@@ -1096,6 +1115,7 @@ public class CrashesTest {
     public void crashInLastSessionCorrupted() throws IOException {
         mockStatic(ErrorLogHelper.class);
         File file = errorStorageDirectory.newFile("last-error-log.json");
+        new FileWriter(file).append("fake_data").close();
         when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{file});
         when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]);
         when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(file);
@@ -1345,7 +1365,9 @@ public class CrashesTest {
         when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenReturn(mock(Device.class));
         ErrorReport report = new ErrorReport();
         mockStatic(ErrorLogHelper.class);
-        when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(mock(File.class));
+        File errorLogFile = mock(File.class);
+        when(errorLogFile.length()).thenReturn(1L);
+        when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(errorLogFile);
         when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{mock(File.class)});
         when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[]{minidumpFile});
         File pendingDir = mock(File.class);
diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/http/DefaultHttpClient.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/http/DefaultHttpClient.java
index 0f65b8ecc..eb0d68ac2 100644
--- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/http/DefaultHttpClient.java
+++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/http/DefaultHttpClient.java
@@ -98,6 +98,11 @@ public class DefaultHttpClient implements HttpClient {
      */
     private static final int MIN_GZIP_LENGTH = 1400;
 
+    /**
+     * Maximum payload length to use prettify for logging.
+     */
+    private static final int MAX_PRETTIFY_LOG_LENGTH = 4 * 1024;
+
     /**
      * Pattern used to replace token in url encoded parameters.
      */
@@ -215,14 +220,18 @@ public class DefaultHttpClient implements HttpClient {
             /* Send payload. */
             if (binaryPayload != null) {
 
-                /* Compress payload if large enough to be worth it. */
+                /* Log payload. */
                 if (AppCenterLog.getLogLevel() <= Log.VERBOSE) {
-                    payload = TOKEN_REGEX_URL_ENCODED.matcher(payload).replaceAll("token=***");
-                    if (CONTENT_TYPE_VALUE.equals(headers.get(CONTENT_TYPE_KEY))) {
-                        payload = new JSONObject(payload).toString(2);
+                    if (payload.length() < MAX_PRETTIFY_LOG_LENGTH) {
+                        payload = TOKEN_REGEX_URL_ENCODED.matcher(payload).replaceAll("token=***");
+                        if (CONTENT_TYPE_VALUE.equals(headers.get(CONTENT_TYPE_KEY))) {
+                            payload = new JSONObject(payload).toString(2);
+                        }
                     }
                     AppCenterLog.verbose(LOG_TAG, payload);
                 }
+
+                /* Compress payload if large enough to be worth it. */
                 if (shouldCompress) {
                     ByteArrayOutputStream gzipBuffer = new ByteArrayOutputStream(binaryPayload.length);
                     GZIPOutputStream gzipStream = new GZIPOutputStream(gzipBuffer);
['sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java', 'sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java', 'CHANGELOG.md', 'sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java', 'sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java', 'apps/sasquatch/src/main/res/values/crashes.xml', 'sdk/appcenter/src/main/java/com/microsoft/appcenter/http/DefaultHttpClient.java', 'apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java']