optimizely/android-sdk

Unable to parse null datafile.

jia-tse-zocdoc opened this issue · 6 comments

Hi,

We were previously on an older SDK 2.1.0 and made the jump to 3.0.0 and started noticing these kinds of error show up in our logs. Here is an example of a stacktrace. It's not reproducible locally, and only affects a small percentage of our users. Not sure what's causing it, but we believe the issue is not related to internet connectivity.

Caused by com.optimizely.ab.config.parser.ConfigParseException: Unable to parse null datafile.
       at com.optimizely.ab.config.ProjectConfig$Builder.build + 605(ProjectConfig.java:605)
       at com.optimizely.ab.Optimizely.initialize + 119(Optimizely.java:119)
       at com.optimizely.ab.Optimizely$Builder.build + 941(Optimizely.java:941)
       at com.optimizely.ab.android.sdk.OptimizelyManager.buildOptimizely + 501(OptimizelyManager.java:501)
       at com.optimizely.ab.android.sdk.OptimizelyManager.injectOptimizely + 445(OptimizelyManager.java:445)
       at com.optimizely.ab.android.sdk.OptimizelyManager$2.onDatafileLoaded + 343(OptimizelyManager.java:343)
       at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded + 78(DefaultDatafileHandler.java:78)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify + 81(DatafileLoader.java:81)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000 + 35(DatafileLoader.java:35)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 134(DatafileLoader.java:134)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 85(DatafileLoader.java:85)
       at android.os.AsyncTask.finish + 695(AsyncTask.java:695)
       at android.os.AsyncTask.-wrap1(AsyncTask.java)
       at android.os.AsyncTask$InternalHandler.handleMessage + 712(AsyncTask.java:712)
       at android.os.Handler.dispatchMessage + 106(Handler.java:106)
       at android.os.Looper.loop + 164(Looper.java:164)
       at android.app.ActivityThread.main + 6626(ActivityThread.java:6626)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run + 438(RuntimeInit.java:438)
       at com.android.internal.os.ZygoteInit.main + 811(ZygoteInit.java:811)

Hi @jia-tse-zocdoc,
Thanks for reporting this. I have a few questions if you don't mind.

  1. Do you happen to have any of the log messages around that?
  2. Do you know which parser you are using (e.g. gson, jackson, json.org)?
  3. Is there a reason you are not on 3.0.1?

@jia-tse-zocdoc What is happening is that the datafile is coming back null, and then it looks for one in cache or load from bundle and that is also null. I suspect that this might be a bad project id or sdk key.

@thomaszurkan-optimizely

It does look like we're on 3.0.1..., but the error is still happening. It started with our transition into 3.0.0

Here's additional logging from the same crash:

Non-fatal Exception: com.zocdoc.android.exception.OptimizelyException
       at com.zocdoc.android.ab.optimizely.OptErrorHandler.handleError + 10(OptErrorHandler.java:10)
       at com.optimizely.ab.Optimizely.initialize + 125(Optimizely.java:125)
       at com.optimizely.ab.Optimizely$Builder.build + 941(Optimizely.java:941)
       at com.optimizely.ab.android.sdk.OptimizelyManager.buildOptimizely + 501(OptimizelyManager.java:501)
       at com.optimizely.ab.android.sdk.OptimizelyManager.injectOptimizely + 445(OptimizelyManager.java:445)
       at com.optimizely.ab.android.sdk.OptimizelyManager$2.onDatafileLoaded + 343(OptimizelyManager.java:343)
       at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded + 78(DefaultDatafileHandler.java:78)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify + 81(DatafileLoader.java:81)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000 + 35(DatafileLoader.java:35)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 134(DatafileLoader.java:134)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 85(DatafileLoader.java:85)
       at android.os.AsyncTask.finish + 695(AsyncTask.java:695)
       at android.os.AsyncTask.access$600 + 180(AsyncTask.java:180)
       at android.os.AsyncTask$InternalHandler.handleMessage + 712(AsyncTask.java:712)
       at android.os.Handler.dispatchMessage + 106(Handler.java:106)
       at android.os.Looper.loop + 193(Looper.java:193)
       at android.app.ActivityThread.main + 6718(ActivityThread.java:6718)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run + 493(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main + 858(ZygoteInit.java:858)
Caused by com.optimizely.ab.OptimizelyRuntimeException: com.optimizely.ab.config.parser.ConfigParseException: Unable to parse null datafile.
       at com.optimizely.ab.Optimizely.initialize + 125(Optimizely.java:125)
       at com.optimizely.ab.Optimizely$Builder.build + 941(Optimizely.java:941)
       at com.optimizely.ab.android.sdk.OptimizelyManager.buildOptimizely + 501(OptimizelyManager.java:501)
       at com.optimizely.ab.android.sdk.OptimizelyManager.injectOptimizely + 445(OptimizelyManager.java:445)
       at com.optimizely.ab.android.sdk.OptimizelyManager$2.onDatafileLoaded + 343(OptimizelyManager.java:343)
       at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded + 78(DefaultDatafileHandler.java:78)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify + 81(DatafileLoader.java:81)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000 + 35(DatafileLoader.java:35)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 134(DatafileLoader.java:134)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 85(DatafileLoader.java:85)
       at android.os.AsyncTask.finish + 695(AsyncTask.java:695)
       at android.os.AsyncTask.access$600 + 180(AsyncTask.java:180)
       at android.os.AsyncTask$InternalHandler.handleMessage + 712(AsyncTask.java:712)
       at android.os.Handler.dispatchMessage + 106(Handler.java:106)
       at android.os.Looper.loop + 193(Looper.java:193)
       at android.app.ActivityThread.main + 6718(ActivityThread.java:6718)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run + 493(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main + 858(ZygoteInit.java:858)
Caused by com.optimizely.ab.config.parser.ConfigParseException: Unable to parse null datafile.
       at com.optimizely.ab.config.ProjectConfig$Builder.build + 605(ProjectConfig.java:605)
       at com.optimizely.ab.Optimizely.initialize + 119(Optimizely.java:119)
       at com.optimizely.ab.Optimizely$Builder.build + 941(Optimizely.java:941)
       at com.optimizely.ab.android.sdk.OptimizelyManager.buildOptimizely + 501(OptimizelyManager.java:501)
       at com.optimizely.ab.android.sdk.OptimizelyManager.injectOptimizely + 445(OptimizelyManager.java:445)
       at com.optimizely.ab.android.sdk.OptimizelyManager$2.onDatafileLoaded + 343(OptimizelyManager.java:343)
       at com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler$1.onDatafileLoaded + 78(DefaultDatafileHandler.java:78)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.notify + 81(DatafileLoader.java:81)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader.access$000 + 35(DatafileLoader.java:35)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 134(DatafileLoader.java:134)
       at com.optimizely.ab.android.datafile_handler.DatafileLoader$RequestDatafileFromClientTask.onPostExecute + 85(DatafileLoader.java:85)
       at android.os.AsyncTask.finish + 695(AsyncTask.java:695)
       at android.os.AsyncTask.access$600 + 180(AsyncTask.java:180)
       at android.os.AsyncTask$InternalHandler.handleMessage + 712(AsyncTask.java:712)
       at android.os.Handler.dispatchMessage + 106(Handler.java:106)
       at android.os.Looper.loop + 193(Looper.java:193)
       at android.app.ActivityThread.main + 6718(ActivityThread.java:6718)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run + 493(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main + 858(ZygoteInit.java:858)

Regarding your question on the parser -- how do I find out? When initializing the optimizely manager, we didn't specify a parser to use.

Hi @thomaszurkan-optimizely! I investigated this a bit and looks like this can happen in case of corrupted datafile cache.

https://github.com/optimizely/android-sdk/blob/master/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java#L282

basically isDatafileCached just checks if the file is in the filesystem.

DatafileCache class is called from AsyncTask and it calls this.context.openFileOutput(filename, 0); inside try-catch-finally block. So at least an empty file is created. If the data is initially corrupted or fileOutputStream.write(data.getBytes()) doesn't finish correctly, this means that during the DatafileCache.load execution you read a String from the file, create a JSONObject from it, and call JSONObject.toString(), as a result the null value is returned and is used for OptimizelyClient initialization.

So i believe you can just check if datafileHandler.loadSavedDatafile(context, datafileConfig); returned null and fallback to locally saved raw resource

@sergey-babakov-zocdoc I think you are correct. But, I think it might be better to not set the last modified header if the cached datafile is corrupt and get a new one (since we are already hitting the cd). I will create a fix for this right now. Thanks so much!

Addressed with #280