ASP.NET Core : Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead

asp.net Mvc

asp.net Mvc Problem Overview


ASP.NET core server, AllowSynchronousIO is set to false

        new WebHostBuilder()
        .UseKestrel(options =>
        {
            options.AllowSynchronousIO = false;
        })

In the action, it outputs a JsonResult

    public async Task<IActionResult> SanityCheck()
    {
        Dictionary<string, string> dic = await GetDic();

        return this.Json(dic);
    }

And it ends with an exception

> System.InvalidOperationException: Synchronous operations are > disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.

Can't I return a JsonResult with AllowSynchronousIO=false ?

asp.net Mvc Solutions


Solution 1 - asp.net Mvc

You might have the following problem: https://github.com/aspnet/AspNetCore/issues/8302

And you can find more info here: https://github.com/aspnet/AspNetCore/issues/7644

A workaround until the issue is being solved is to allow Synchronous IO. Put this in Startup.cs for either Kestrel or IIS:

public void ConfigureServices(IServiceCollection services)
{
    // If using Kestrel:
    services.Configure<KestrelServerOptions>(options =>
    {
        options.AllowSynchronousIO = true;
    });

    // If using IIS:
    services.Configure<IISServerOptions>(options =>
    {
        options.AllowSynchronousIO = true;
    });
}

Solution 2 - asp.net Mvc

When the exception is thrown in code you cannot control, and you have no other choice than to enable AllowSynchronousIO, it is best to enable it for specific requests, instead of globally.

In the GitHub issue announcing this feature, the following workaround is suggested:

var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
    syncIOFeature.AllowSynchronousIO = true;
}

You can create a simple middleware function to apply this to specific requests:

app.Use(async (context, next) =>
{
    if (context.Request.Path.StartsWithSegments("/some-endpoint-that-needs-sync-io"))
    {
        var syncIoFeature = context.Features.Get<IHttpBodyControlFeature>();
        if (syncIoFeature != null)
        {
            syncIoFeature.AllowSynchronousIO = true;
        }
    }

    await next();
})

Solution 3 - asp.net Mvc

I had this issue with my unit tests. I had to update my TestServer to AlloSynchronousIO

Server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
Server.AllowSynchronousIO = true;

Solution 4 - asp.net Mvc

I managed to find my own unique version of this problem with the following middleware (.NET 6.0):

public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
	context.Response.StatusCode = 200;
	using (var writer = new StreamWriter(context.Response.Body))
	{
		await writer.WriteLineAsync("Done!");
		return;
	}
}

I spent a long time staring at this until I realised what the stack trace was telling me:

> System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead. > > at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count) > > at System.IO.Stream.Write(ReadOnlySpan`1 buffer) > > at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder) > > at System.IO.StreamWriter.Dispose(Boolean disposing) > > at System.IO.TextWriter.Dispose()

The important line here was the System.IO.TextWriter.Dispose(): this is causing the resulting flush and lower-level write to be called synchronously.

Fortunately, StreamWriter implements IAsyncDisposable so it's easy to solve it by adding await before using:

public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
	context.Response.StatusCode = 200;
	await using (var writer = new StreamWriter(context.Response.Body))
	{
		await writer.WriteLineAsync("Done!");
		return;
	}
}

Hopefully this helps someone not waste as much time as I did.

Solution 5 - asp.net Mvc

Based on Mark Lagendijk's answer, i applied that logic on a declarative way with an attribute:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AllowSynchronousIOAttribute : ActionFilterAttribute
{
	public AllowSynchronousIOAttribute()
	{
	}

	public override void OnResultExecuting(ResultExecutingContext context)
	{
		var syncIOFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
		if (syncIOFeature != null)
		{
			syncIOFeature.AllowSynchronousIO = true;
		}
	}
}

Just use it on an action method or an entire controller to enable it:

[AllowSynchronousIO]
public IActionResult DownloadSynchronous()
{
    return Something();
}

Solution 6 - asp.net Mvc

I'm not sure what your requirements are or what GetDic() does, but code like the following should absolutely work given GetDic() doesn't do any synchronous IO:

public async Task<IActionResult> SanityCheck()
{
    Dictionary<string, string> dic = await GetDic();

    return this.Ok(dic);
}

And if you still want to serialise dic to JSON, the following code should do:

public async Task<IActionResult> SanityCheck()
{
    Dictionary<string, string> dic = await GetDic();
    string json = JsonConvert.SerializeObject(dic);

    return this.Ok(json);
}

Note that this last piece of code returns the result as text/plain instead of application/json.

Also, I tested this under ASP.NET Core 2.2.

Solution 7 - asp.net Mvc

this code work for me, make async read:

public override async void OnActionExecuting(ActionExecutingContext filterContext)
{
	string content = "";
	var request = filterContext.HttpContext.Request;
	try
	{
		request.EnableBuffering();
		request.Body.Position = 0;
		using (StreamReader reader = new StreamReader(request.Body, Encoding.UTF8,true,1024,true))
		{
			content = await reader.ReadToEndAsync();
		}
	}
	finally
	{
		request.Body.Position = 0;
	}
}

Solution 8 - asp.net Mvc

In the configure service add below code. It worked for me

 services.Configure<KestrelServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });

            // If using IIS:
            services.Configure<IISServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });

Solution 9 - asp.net Mvc

Everybody else is showing how to allow synchronous IO. In my case, the problem was that my custom stream classes were not implementing DisposeAsync and FlushAsync correctly. Fixing that made the error go away.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMr.Wang from Next DoorView Question on Stackoverflow
Solution 1 - asp.net MvchightechView Answer on Stackoverflow
Solution 2 - asp.net MvcMark LagendijkView Answer on Stackoverflow
Solution 3 - asp.net MvcrevenView Answer on Stackoverflow
Solution 4 - asp.net MvcDiplomacyNotWarView Answer on Stackoverflow
Solution 5 - asp.net MvcT-motyView Answer on Stackoverflow
Solution 6 - asp.net MvcTheBlueSkyView Answer on Stackoverflow
Solution 7 - asp.net MvcFarshad BayatView Answer on Stackoverflow
Solution 8 - asp.net MvcPavan AmbhureView Answer on Stackoverflow
Solution 9 - asp.net MvcArtur KrajewskiView Answer on Stackoverflow