Skip to content

NullPointerException in SDKCore.recover() permanently blocks SDK initialization when a crash file exists #263

@DanBoSlice

Description

@DanBoSlice

When a crash file ([CLY]_crash_*) exists in the storage directory, every subsequent call to Countly.instance().init(config) throws a NullPointerException because SDKCore.recover() is invoked before the networking field is assigned.

The crash file is never cleaned up after the exception (the deletion line comes after the NPE), so the bug repeats on every application restart.

This is a critical bug. Once a single crash is recorded, the SDK becomes permanently non-functional for that storage directory. All events, sessions, and future crashes accumulate on disk indefinitely. The only recovery is manually deleting the crash file from the filesystem.

Root Cause

In SDKCore.init(InternalConfig), the call order is:

// SDKCore.java — init()
recover(config);                              // internally using networking
networking = new DefaultNetworking();         // networking assigned AFTER

recover() iterates stored crash files and calls processCrash(), which eventually calls Storage.push()networking.check(config). Since networking is still null at this point:

   java.lang.NullPointerException: Cannot invoke "ly.count.sdk.java.internal.Networking.check(ly.count.sdk.java.internal.InternalConfig)" because "this.networking" is null
       at ly.count.sdk.java.internal.SDKCore.onSignal(SDKCore.java:682)
       at ly.count.sdk.java.internal.ModuleRequests.injectParams(ModuleRequests.java:38)
       at ly.count.sdk.java.internal.Storage.pushAsync(Storage.java:218)
       at ly.count.sdk.java.internal.SDKCore.onCrash(SDKCore.java:455)
       at ly.count.sdk.java.internal.CrashImpl.processCrash(CrashImpl.java:93)
       at ly.count.sdk.java.internal.SDKCore.recover(SDKCore.java:493)
       at ly.count.sdk.java.internal.SDKCore.init(SDKCore.java:126)

Steps to Reproduce

  1. Initialize the Countly SDK with crash reporting enabled
  2. Trigger a crash that gets recorded as a [CLY]_crash_* file in the storage directory
  3. Restart the application and call Countly.instance().init(config) again
  4. SDK init throws NPE every time — the crash file is never sent or removed

Expected Behavior

recover() should execute after networking is initialized, or processCrash() should handle a null networking field gracefully. The crash file should be cleaned up even if processing fails, to avoid a permanent init-blocking loop.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions