How to fix RemoteJSDataStream NullReferenceException?

Viewed 265

I get an error while uploading files as StreamContent in parallel from .NET 6 / Blazor Server-Side to API via HttpClient. Here is the stripped down code and the full text of the error:

P.S. The RemoteJSRuntime that is passed to Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSDataStream.ReceiveData by the framework is NULL, so an error is thrown. However, I don't understand why NULL is passed. I cannot catch it, extinguish it, somehow influence it.

P.S.S. It is not related to ApiClient, it works fine. The error only occurs on startup after the first page load, oddly enough, after reloading the page and starting again, everything works.

Source code:

https://github.com/abberdeen/BlazorServer.ParallelFileUpload

Related issue:

https://github.com/dotnet/aspnetcore/issues/38854

        public void AddFilesToUploadQueue(List<FileUploadDto> files)
        {
            foreach (var item in files)
            {
                fileUploadQueue.Enqueue(item);
            }

            for (int i = 0; i < files.Count; i++)
            {
                Task t = factory.StartNew(() => UploadOne());
                tasks.Add(t); 
            }
        } 
        private async Task UploadOne()
        { 
            semaphore.WaitOne();

            FileUploadDto item;

            if (!fileUploadQueue.TryDequeue(out item))
            {
                return;
            } 

            // Make HttpRequest
            await UploadFileAsync(item);
          
            semaphore.Release();
        }
blazor.server.js:1 [2021-12-06T19:07:53.123Z] Error: System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSDataStream.ReceiveData(RemoteJSRuntime runtime, Int64 streamId, Int64 chunkId, Byte[] chunk, String error)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c__11`1.<<InvokeAsync>b__11_0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost.ReceiveJSDataChunk(Int64 streamId, Int64 chunkId, Byte[] chunk, String error)

Errors in the output window:

System.TimeoutException: Did not receive any data in the allotted time.
         at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
         at System.IO.Pipelines.Pipe.GetReadAsyncResult()
         at System.IO.Pipelines.PipeReaderStream.ReadAsyncInternal(Memory`1 buffer, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSDataStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Components.Forms.BrowserFileStream.CopyFileDataIntoBuffer(Int64 sourceOffset, Memory`1 destination, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Components.Forms.BrowserFileStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
         at System.IO.Stream.<CopyToAsync>g__Core|29_0(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
         at System.Net.Http.StreamToStreamCopy.<CopyAsync>g__DisposeSourceAsync|1_0(Task copyTask, Stream source)
         at System.Net.Http.HttpContent.<CopyToAsync>g__WaitAsync|56_0(ValueTask copyTask)
         at System.Net.Http.MultipartContent.SerializeToStreamAsyncCore(Stream stream, TransportContext context, CancellationToken cancellationToken)
         at System.Net.Http.HttpContent.<CopyToAsync>g__WaitAsync|56_0(ValueTask copyTask)
         at System.Net.Http.HttpContent.<CopyToAsync>g__WaitAsync|56_0(ValueTask copyTask)
         at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, Boolean async, CancellationToken cancellationToken)
         at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
         at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
         at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
         at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
         at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
         at ~~~~~BlazorApp.Services.AuthHttpClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in ~~~~~BlazorApp\Services\AuthHttpClientHandler.cs:line 27
         at System.Net.Http.Handlers.ProgressMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
         at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
         at ~~~~~BlazorApp.Services.FileUploadApiService.PostAndReportProgress(String url, MultipartFormDataContent formData, String guid, EventHandler`1 progressHandler, CancellationToken cancellationToken) in ~~~~~BlazorApp\Services\FileUploadApiService.cs:line 106
         --- End of inner exception stack trace ---
         at ~~~~~BlazorApp.Services.FileUploadApiService.PostAndReportProgress(String url, MultipartFormDataContent formData, String guid, EventHandler`1 progressHandler, CancellationToken cancellationToken) in ~~~~~BlazorApp\Services\FileUploadApiService.cs:line 122
         at ~~~~~BlazorApp.Services.FileUploadApiService.ChatUploadFileAsync(StreamContent file, Int32 chatId, String guid, EventHandler`1 progressHandler, CancellationToken cancellationToken) in ~~~~~BlazorApp\Services\FileUploadApiService.cs:line 59
         at ~~~~~BlazorApp.Services.FileUploadManager.UploadFileAsync(FileUploadDto item) in ~~~~~BlazorApp\Services\FileUploadManager.cs:line 171
         at ~~~~~BlazorApp.Services.FileUploadManager.<StartUploadTask>b__18_0() in ~~~~~BlazorApp\Services\FileUploadManager.cs:line 108 

1 Answers

Thanks for the sample. I was able to reproduce the System.TimeoutException.

I am unable to reproduce the error with this code (FileUploadManager changes only). Can you try this?

        public async void AddFilesToUploadQueue(IReadOnlyList<IBrowserFile> files)
        {
            lock (fileUploadQueue)
            {
                foreach (var item in files)
                {
                    fileUploadQueue.Enqueue(item);
                }
            }

            for (int i = 0; i < files.Count; i++)
            {              
                IBrowserFile? file = null;
                lock(fileUploadQueue)
                {
                    fileUploadQueue.TryDequeue(out file);
                }

                tasks.Add(UploadOneFile(file));
            }
            
            //await Task.WhenAll(tasks);
        }

        protected async Task UploadOneFile(IBrowserFile file)
        {
            await semaphoreSlim.WaitAsync();
                       
            try
            {
                using (var readStream = file.OpenReadStream(int.MaxValue))
                {
                    var streamContent = fileUploadService.CreateStreamContent(readStream, file.Name, file.ContentType);

                    var result = await fileUploadService.UploadFileAsync(streamContent);
                }

            }
            finally
            {
                semaphoreSlim.Release();
            }
        }
Related