Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvalidOperationException on LdClient.InitAsync operation (when used in Unity for mobile builds) #56

Open
pavel-shchelkun-ilogos opened this issue Nov 10, 2023 · 5 comments

Comments

@pavel-shchelkun-ilogos
Copy link

pavel-shchelkun-ilogos commented Nov 10, 2023

We are trying to use LaunchDarkly .NET client SDK in Unity 2019.4.34f1 for mobile platforms (Android and iOS).
The SDK works in Unity Editor, but when run in builds it throws InvalidOperationException from LdClient.InitAsync operation.
All builds are compiled without issues.

To reproduce

Use SDK in Unity 2019.4.34f1 for mobile platforms (Android and iOS).

Expected behavior

LdClient initializes without issues and works as expected.

Logs

2023-11-10 18:35:06.838 +02:00 [LaunchDarkly.Sdk] INFO: Starting LaunchDarkly Client 4.0.0.0
2023-11-10 18:35:06.839 +02:00 [LaunchDarkly.Sdk.DataStore] ERROR: Failure in persistent data store: System.UnauthorizedAccessException: Access to the path "/private/var/mobile/Containers/Data/Application/FEF3472F-0473-4B7B-8EE8-4CF395AD5ED4/.config" is denied.
2023-11-10 18:35:06.844 +02:00 [LaunchDarkly.Sdk] INFO: Network availability is now True
InvalidOperationException: The converter specified on 'LaunchDarkly.Sdk.Context' does not derive from JsonConverter or have a public parameterless constructor.
  at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid (System.Type classType, System.Reflection.MemberInfo memberInfo) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.JsonSerializerOptions.GetConverterFromAttribute (System.Text.Json.Serialization.JsonConverterAttribute converterAttribute, System.Type typeToConvert, System.Type classTypeAttributeIsOn, System.Reflection.MemberInfo memberInfo) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.JsonSerializerOptions.GetConverterInternal (System.Type typeToConvert) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.JsonSerializerOptions.DetermineConverter (System.Type parentClassType, System.Type runtimePropertyType, System.Reflection.MemberInfo memberInfo) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.Serialization.Metadata.JsonTypeInfo.GetConverter (System.Type type, System.Type parentClassType, System.Reflection.MemberInfo memberInfo, System.Type& runtimeType, System.Text.Json.JsonSerializerOptions options) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.Serialization.Metadata.JsonTypeInfo..ctor (System.Type type, System.Text.Json.JsonSerializerOptions options) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.JsonSerializerOptions.<InitializeForReflectionSerializer>g__CreateJsonTypeInfo|112_0 (System.Type type, System.Text.Json.JsonSerializerOptions options) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Func`3[T1,T2,TResult].Invoke (T1 arg1, T2 arg2) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.JsonSerializerOptions.GetOrAddClass (System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.JsonSerializer.GetTypeInfo (System.Text.Json.JsonSerializerOptions options, System.Type runtimeType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Text.Json.JsonSerializer.Serialize[TValue] (TValue value, System.Text.Json.JsonSerializerOptions options) [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Json.LdJsonSerialization.SerializeObject[T] (T instance) [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.Internal.DataModelSerialization.SerializeContext (LaunchDarkly.Sdk.Context context) [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.Internal.DataSources.StreamingDataSource.Start () [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.Internal.DataSources.ConnectionManager.OpenOrCloseConnectionIfNecessary (System.Boolean mustReinitializeDataSource) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Func`1[TResult].Invoke () [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.Internal.LockUtils.WithWriteLock[T] (System.Threading.ReaderWriterLockSlim rwLock, System.Func`1[TResult] fn) [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.LdClient+<StartAsync>d__41.MoveNext () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine] (TStateMachine& stateMachine) [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.LdClient.StartAsync () [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.LdClient+<InitAsync>d__48.MoveNext () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[TResult].Start[TStateMachine] (TStateMachine& stateMachine) [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.LdClient.InitAsync (LaunchDarkly.Sdk.Client.Configuration config, LaunchDarkly.Sdk.Context initialContext) [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.LdClient+<InitAsync>d__44.MoveNext () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[TResult].Start[TStateMachine] (TStateMachine& stateMachine) [0x00000] in <00000000000000000000000000000000>:0 
  at LaunchDarkly.Sdk.Client.LdClient.InitAsync (System.String mobileKey, LaunchDarkly.Sdk.Client.ConfigurationBuilder+AutoEnvAttributes autoEnvAttributes, LaunchDarkly.Sdk.Context initialContext) [0x00000] in <00000000000000000000000000000000>:0 

SDK version

We used NuGet to install it in the project:
https://www.nuget.org/packages/LaunchDarkly.ClientSdk/4.0.0
NuGet\Install-Package LaunchDarkly.ClientSdk -Version 4.0.0

image

OS/platform
Android, iOS

Additional context

The exception looks very strange for a few reasons:

  • Context is a struct and cannot have public parameterless constructor
  • LdJsonConverters.ContextConverter derives from JsonConverter<Context>

We also use Context.FromUser to create Context object, mainly to be able to set anonymous value. This is needed to match original code that we are porting.

@tanderson-ld
Copy link
Contributor

Hi @pavel-shchelkun-ilogos , a few thoughts and questions that come to my mind immediately.

General questions:

  • Could you provide a snippet of your configuration and initialization code?
  • Do you serialize the Context in a callback or custom implementation that you have provided to the SDK?
  • Have you ever seen that persistent data access error you included? I wonder if that error is related. I haven't worked too much on Unity. When you updated to 4.0.0, did any of the filesystem permissions on the project change?

2023-11-10 18:35:06.839 +02:00 [LaunchDarkly.Sdk.DataStore] ERROR: Failure in persistent data store: System.UnauthorizedAccessException: Access to the path "/private/var/mobile/Containers/Data/Application/FEF3472F-0473-4B7B-8EE8-4CF395AD5ED4/.config" is denied.

  • Do you have any minimizer/minifier running on the project? I wonder if that could be interfering with the JSON annotation system. I don't have high confidence this would be the issue, just worth knowing.

Unrelated to this issue:

  • User is going to be deprecated soon, it may make sense while you are working on this to just use the ContextBuilder to make a Context directly instead of using User and Context.FromUser

@pavel-shchelkun-ilogos
Copy link
Author

Hello @tanderson-ld, thank you for responding to this.

I will try to answer your questions here.

Could you provide a snippet of your configuration and initialization code?

https://gist.github.com/pavel-shchelkun-ilogos/9e3b6f3977d995e6a3ed52d2b01e9784

Do you serialize the Context in a callback or custom implementation that you have provided to the SDK?

We do not serialize it, just creating it and passing to LdClient.InitAsync or ldClient.IdentifyAsync. No custom implementation was provided, we are not aware of that possibility... please point us to a documentation where that is described. Ideally, we could switch back to Newtonsoft.Json as we are using it in the project, but for now we are using LdJsonSerialization.SerializeObject to get json and then deserializing that json with our jsonService that uses Newtonsoft.Json internally. That is only done like that for LdValueType.Array and LdValueType.Object. Basically, all the code using launchdarkly-dotnet-client-sdk is in the snippet above.

Have you ever seen that persistent data access error you included? I wonder if that error is related. I haven't worked too much on Unity. When you updated to 4.0.0, did any of the filesystem permissions on the project change?

No, we haven't seen that before. But if SDK handles local storage in some specific way that differs from Unity then we need to address it as well by providing our own implementation of the storage if possible.

Do you have any minimizer/minifier running on the project? I wonder if that could be interfering with the JSON annotation system. I don't have high confidence this would be the issue, just worth knowing.

I suppose there is something like that since the project when built gets converted into IL2CPP in both Android and iOS builds. The code stripping is also present. But the SDK libs seem to be getting into the build correctly.

User is going to be deprecated soon, it may make sense while you are working on this to just use the ContextBuilder to make a Context directly instead of using User and Context.FromUser

Please see the snippet provided above. As I mentioned before, we are porting an existing work that uses anonymous flag and the only way to set that flag for now is to use User and Context.FromUser. We might be missing on something here with the latest changes in LaunchDarkly SDK and we might need to revisit this later for sure, but for now to match the original code we do it like that. Mainly to distinct logged in user and not logged in player/app state.

This is a critical issue at the moment for us and we are looking for options to make LaunchDarkly work in Unity 2019.4.34f1.

@tanderson-ld
Copy link
Contributor

Are you already using a version of the LaunchDarkly Dotnet SDK in Unity? We don't have official support in Unity. My initial interpretation was that you were already using 2.x or 3.x of the LaunchDarkly Dotnet SDK and started seeing this issue when updating to 4.0.

@pavel-shchelkun-ilogos
Copy link
Author

Are you already using a version of the LaunchDarkly Dotnet SDK in Unity? We don't have official support in Unity. My initial interpretation was that you were already using 2.x or 3.x of the LaunchDarkly Dotnet SDK and started seeing this issue when updating to 4.0.

This is the first time we tried using it. The project that we are porting from is in Angular (typescript), so it uses Javascript SDK. We just hoped we could make it work similar to this attempt https://github.com/Mohawk-Valley-Interactive/launchdarkly-client-unity-plugin but with latest version of SDK.

@tanderson-ld
Copy link
Contributor

It looks like that plugin has some serialization logic inside of it. Perhaps it needs to be updated to handle the context object. Unfortunately with other team priorities, I can't spend much time helping since it isn't an officially supported platform.

@cwaldren-ld cwaldren-ld transferred this issue from launchdarkly/dotnet-client-sdk Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants