Unity Client AddFriendsAsync method returns error

Hi again :slight_smile:

I am attempting to use the feature of Nakama where users can add friends to their friend list.

My Unity code is as follows to send the request (this is taken from the Pirate Panic Nakama example):

public async void AddFriend()
		{
			try
			{
				string[] usernames = { _usernameSearcher.InputFieldValue };
				
				await _connection.Client.AddFriendsAsync(_connection.Session, new string[] {}, usernames);
				ActualizeFriendsList();
			}
			catch (Exception e) //catching exception, if program entered this code adding friend operation was not successfully completed
			{
				Debug.LogError("Adding friend failed (" + e.Message + ")");
			}
		}

When sending the friend request, this error is sent back:

Cannot create a data handler without payload data

The full error stacktrace:

at UnityEngine.Networking.UploadHandlerRaw..ctor (System.Byte[] data) [0x00017] in <39536a03ec01488d9573f19141ef4b8b>:0 
  at Nakama.UnityWebRequestAdapter.BuildRequest (System.String method, System.Uri uri, System.Collections.Generic.IDictionary`2[TKey,TValue] headers, System.Byte[] body, System.Int32 timeout) [0x00023] in C:\Users\User\Unity\relic-nakama\Relic\Assets\Nakama\Runtime\UnityWebRequestAdapter.cs:91 
  at Nakama.UnityWebRequestAdapter.SendAsync (System.String method, System.Uri uri, System.Collections.Generic.IDictionary`2[TKey,TValue] headers, System.Byte[] body, System.Int32 timeout, System.Nullable`1[T] cancellationToken) [0x00007] in C:\Users\User\Unity\relic-nakama\Relic\Assets\Nakama\Runtime\UnityWebRequestAdapter.cs:76 
  at Nakama.ApiClient+<AddFriendsAsync>d__42.MoveNext () [0x0012f] in <3930c37f95f7433fa71239b42efdd1f6>:0 
--- End of stack trace from previous location where exception was thrown ---
  at Nakama.RetryInvoker+<InvokeWithRetry>d__7.MoveNext () [0x001a6] in <3930c37f95f7433fa71239b42efdd1f6>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at Nakama.Client+<AddFriendsAsync>d__39.MoveNext () [0x001cc] in <3930c37f95f7433fa71239b42efdd1f6>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at Relic.FriendsMenuUI+<AddFriend>d__25.MoveNext () [0x00076] in C:\Users\User\Unity\relic-nakama\Relic\Assets\Relic\Scripts\Menus\FriendsMenuUI.cs:218 

Thank you for the help :laughing:

  1. Versions: Nakama {3.11.0}, {Docker}, {Unity Client 3.3.0}
  2. Server Framework Runtime language (If relevant) TS/JS}

I figured this out if anyone else is having this problem:

In Assets/Nakama/Runtime/UnityWebRequestAdapter.cs, there is a method that builds the requests called BuildRequest, around line 64.

When the Nakama Client.AddFriendsAsync is called, it does a POST request with a null body argument - there is problem.

Sending a null body to new UploadHandlerRaw(body) causes the error

Cannot create a data handler without payload data

because the code for the UploadHandlerRaw looks like this

    /// <summary>
    ///   <para>General constructor. Contents of the input argument are copied into a native buffer.</para>
    /// </summary>
    /// <param name="data">Raw data to transmit to the remote server.</param>
    public unsafe UploadHandlerRaw(byte[] data)
    {
      // ***if data is null, this throws an error!!!***
      this.m_Payload = data != null && data.Length != 0 ? new NativeArray<byte>(data, Allocator.Persistent) : throw new ArgumentException("Cannot create a data handler without payload data");
      this.m_Ptr = UploadHandlerRaw.Create(this, (byte*) this.m_Payload.GetUnsafeReadOnlyPtr<byte>(), this.m_Payload.Length);
    }

Updating the call to put at least one byte into the body if it is null fixes this problem.

The modifications look like this

private static UnityWebRequest BuildRequest(string method, Uri uri, IDictionary<string, string> headers,
            byte[] body, int timeout)
        {
            UnityWebRequest www;

            // ***ADD THIS LINE to make body not null
            body ??= new byte[] {0};
            
            if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(method, "PUT", StringComparison.OrdinalIgnoreCase))
            {
                www = new UnityWebRequest(uri, method)
                {
                    uploadHandler = new UploadHandlerRaw(body),
                    downloadHandler = new DownloadHandlerBuffer()
                };
            }
            else if (string.Equals(method, "DELETE", StringComparison.OrdinalIgnoreCase))
            {
                www = UnityWebRequest.Delete(uri);
            }
            else
            {
                www = UnityWebRequest.Get(uri);
            }

            www.SetRequestHeader("Content-Type", "application/json");
            foreach (var kv in headers)
            {
                www.SetRequestHeader(kv.Key, kv.Value);
            }

            www.timeout = timeout;
            return www;
        }

and now I can add friends :laughing:

1 Like