All articles
.NET/November 3, 2026/8 min read

10 November: What Actually Breaks When .NET 8 and In-Process Azure Functions Run Out of Support

.NET 8, .NET 9 and the Azure Functions in-process model all reach end of support on 10 November 2026. What is confirmed, what happens to running apps, how to find what you run, and the JSON attribute trap that fails without any error.

Introduction

For many .NET teams, 10 November 2026 is a date with several deadlines on it at the same time. Both .NET 8 and .NET 9 reach the end of support on that day, and the in-process model of Azure Functions does too. It is also the date in the CISA catalog for a Brotli vulnerability that affects .NET. Additionally, Microsoft has planned the launch of .NET 11 for the same week, so it is very easy to mix these things together.

In this article I want to separate what is confirmed from what people assume, because "end of support" sounds more dramatic than what actually happens on the day. Then we will examine the migration trap which, in my view, is the most dangerous one, because nothing fails when you hit it. All the facts here are as of 3 November 2026, and the sources are listed at the end.

What is confirmed about the dates

According to the official .NET support policy, .NET 8 (LTS) and .NET 9 (STS) both end on 10 November 2026. That is because .NET 9 was originally planned to end in May 2026, but Microsoft extended STS releases to 24 months, which moved it to the same day as .NET 8. The latest patches right now are 8.0.33 and 9.0.22, both from 25 October. .NET 10 is an LTS release and is supported until 14 November 2028.

About .NET 11, we should be careful. The .NET blog says that .NET Conf runs from 10 to 12 November and that .NET 11 launches at this event. Additionally, the release notes state that .NET 11 is supported from 10 November 2026 to 9 November 2028. However, the latest published build today is 11.0.0-rc.3, and the Microsoft pages listed below do not give the exact day of the final build. The Azure Functions documentation does not list .NET 11 yet, and the migration guide tells you to target .NET 10.

What actually happens to your running apps

In short, your apps do not stop. The retirement notice says that you may still use your .NET apps with the in-process model beyond 10 November 2026, but they will no longer receive security and feature updates from Microsoft. The Azure Functions language support policy describes this phase in a very similar way. Apps on retired versions can still be created and deployed, and they continue to run, although they are not eligible for new features, security patches and performance optimizations. There is one more sentence in that policy which is easy to miss, because it says that Microsoft may limit these apps in certain cases, including limiting scaling to one instance. Additionally, you are required to upgrade before you can receive support for the app.

Therefore, the risk after this date is not an outage on the morning of the 11th. The risk is that the next vulnerability has no patch, and that a performance problem has no support ticket behind it. For a plain .NET 8 or .NET 9 application outside Functions, the situation is similar, since the support policy says that out-of-support versions may put your applications and data at risk.

Find what you are running

Before planning anything, you need an inventory, and fortunately this part is easy. On a machine or inside a container image, this command lists the installed runtimes:

dotnet --list-runtimes

In your repositories, the target framework and the Functions model are both visible in the project files. A reference to Microsoft.NET.Sdk.Functions means in-process, while Microsoft.Azure.Functions.Worker means isolated:

grep -rE --include='*.csproj' --include='*.props' '<TargetFrameworks?>' .
grep -rlE --include='*.csproj' 'Microsoft\.NET\.Sdk\.Functions' .

In Azure, the value that matters is the FUNCTIONS_WORKER_RUNTIME application setting, where dotnet is the in-process model and dotnet-isolated is the isolated worker model. This loop prints the value for every function app in the current subscription:

az functionapp list --query "[].{name:name, group:resourceGroup}" -o tsv |
while IFS=$'\t' read -r name group; do
  runtime=$(az functionapp config appsettings list -n "$name" -g "$group" \
    --query "[?name=='FUNCTIONS_WORKER_RUNTIME'].value | [0]" -o tsv)
  echo "$name $runtime"
done

Do not forget the isolated apps which still target .NET 8 or .NET 9. They are out of support on the same day, although they only need a target framework update and not a model migration.

The trap: your JSON attributes stop working

The in-process model used Newtonsoft.Json, and the isolated worker model uses System.Text.Json by default. These two libraries have attributes with similar purposes, but System.Text.Json simply ignores the Newtonsoft ones. Microsoft's migration guide says that the affected property is bound to its default value without reporting an error. To see this on the real host, let's take a very ordinary model:

using Newtonsoft.Json;
 
public class OrderRequest
{
    [JsonProperty("order_id")]
    public string OrderId { get; set; }
 
    [JsonProperty("total_amount")]
    public decimal TotalAmount { get; set; }
 
    [JsonIgnore]
    public string InternalNote { get; set; }
}

Here is the function in the in-process model on .NET 8:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
 
public static class CreateOrder
{
    [FunctionName("CreateOrder")]
    public static IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post")] OrderRequest order)
    {
        order.InternalNote = "margin below target";
        return new OkObjectResult(order);
    }
}

And here is the same function after a migration to the isolated model on .NET 10 with ASP.NET Core integration. The model class has not been touched, and the project still compiles because Newtonsoft.Json is still referenced:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute;
 
public class CreateOrder
{
    [Function("CreateOrder")]
    public IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
        [FromBody] OrderRequest order)
    {
        order.InternalNote = "margin below target";
        return new OkObjectResult(order);
    }
}

To compare the two, we can post the same body, {"order_id":"A-100","total_amount":49.90}, to both functions, running them in Microsoft's own container images for the Functions host. This is the response from the in-process function:

HTTP 200
{"order_id":"A-100","total_amount":49.90}

And this is the response from the isolated function, with the same model and the same request:

HTTP 200
{"orderId":null,"totalAmount":0,"internalNote":"margin below target"}

The status is still 200, and the host log has no error for this request. However, three things have changed at the same time. First, both values from the request are lost, so if this function wrote the order to a database, it would write an empty ID and a total of zero. Second, the field names in the response have changed, so your clients receive a different contract. Third, the [JsonIgnore] property which was hidden is now sent to the caller, and this can be a very serious problem if that property holds internal data.

Now let's examine the two ways to fix it. The first one is to replace the attributes with their System.Text.Json equivalents, which is the cleaner option when you have a small number of models. Note that [JsonIgnore] keeps its name and only the namespace changes, which is why a quick look during a code review does not catch it:

using System.Text.Json.Serialization;
 
public class OrderRequest
{
    [JsonPropertyName("order_id")]
    public string OrderId { get; set; }
 
    [JsonPropertyName("total_amount")]
    public decimal TotalAmount { get; set; }
 
    [JsonIgnore]
    public string InternalNote { get; set; }
}

The second one keeps the old model and tells ASP.NET Core to use Newtonsoft, by adding the Microsoft.AspNetCore.Mvc.NewtonsoftJson package and one line in Program.cs:

using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
 
var builder = FunctionsApplication.CreateBuilder(args);
 
builder.ConfigureFunctionsWebApplication();
 
builder.Services.AddMvc().AddNewtonsoftJson();
 
builder.Build().Run();

With either fix, the response was the same as the in-process one. However, be careful with the second option, because this line only configures the HTTP layer. According to the documentation, ASP.NET Core has its own serialization layer, and the general worker serializer is configured separately through WorkerOptions. Code that uses HttpRequestData goes through that worker serializer, and ReadFromJsonAsync<T>() on HttpRequest is not covered by this line either. The test above covers only the HTTP path, so please test your queue and other bindings with real payloads.

Other traps in the migration

There are a few more points from the documentation that can affect your plan:

  • Linux Consumption. You can't run .NET 10 on Linux in the Consumption plan, and .NET 9 is the last version it receives. Since .NET 9 also ends on 10 November, these apps need to move to the Flex Consumption plan in order to reach a supported version. Additionally, Flex Consumption doesn't support the in-process model, and it doesn't currently support deployment slots.
  • The order of the two changes. Changing FUNCTIONS_WORKER_RUNTIME to dotnet-isolated and publishing the migrated code are two separate restarts, and between them the app fails with the AZFD0013 mismatch error. Microsoft recommends a staging slot so that this state stays out of production, and you should not mark the setting as a slot setting.
  • Synchronous I/O. With ASP.NET Core integration, synchronous reads and writes on the request and response streams can fail with InvalidOperationException, so ReadToEnd needs to become ReadToEndAsync.
  • Logging. In the isolated model, host.json controls only the logs of the host, so your log filters for your own code need to move to Program.cs.

CVE-2026-111497 has the same deadline

This one is separate from the end of support, but it has the same date. CISA added CVE-2026-111497, a vulnerability in the Google Brotli decoder, to its Known Exploited Vulnerabilities catalog on 27 October with a due date of 10 November. Microsoft shipped an out-of-band fix on 25 October in 8.0.33, 9.0.22, 10.0.14 and 11.0.0-rc.3, and rates it CVSS 8.1 for .NET.

Specifically, Microsoft says that an application is exposed only if it decompresses untrusted Brotli data. That means ASP.NET Core request decompression with Brotli, HttpClient automatic decompression configured with DecompressionMethods.Brotli, or direct use of APIs like BrotliStream. These automatic features are not enabled by default, and .NET Framework is not affected. If you do use them, self-contained applications must be rebuilt and redeployed, because updating the machine does not change the runtime inside them. In my view, this is also a very good reason to install these patches before the 10th, even if your migration takes longer.

References

Share