This is about the host's own startup validation path, the one that emits AZFD0013, not about deployment or slot mechanics. The state it reproduces from is a single configuration mismatch: FUNCTIONS_WORKER_RUNTIME set to dotnet while the deployed payload is an isolated worker payload. The steps below are only how the app arrived in that state.
Investigative information
- Timestamp:
August 25 and 26, 2026. The swap quoted below ran from 18:09:21 to 18:10:38
- Function App version:
FUNCTIONS_EXTENSION_VERSION ~4. Two host extensions were loaded across the swap, SiteExtensions\Functions\4.1053.200 before and SiteExtensions\FunctionsInProc\4.641.300 after
- Function App name / Invocation ID:
The app and its Application Insights resource have since been deleted, so there is nothing left to look up. The report rests on the host source and on the logs quoted below, which I kept. If a live repro with telemetry would help, I can stand the setup up again: two slots, one payload each, one swap.
- Function name(s):
One HTTP-triggered function plus a diagnostics endpoint that exists only in the isolated payload.
- Region:
West Europe
- OS:
Windows
Repro steps
- A function app with a production and a staging slot. Production holds an in-process .NET 8 payload with
FUNCTIONS_WORKER_RUNTIME=dotnet and netFrameworkVersion v8.0. Staging holds an isolated .NET 10 payload on Microsoft.Azure.Functions.Worker 2.52.0 with dotnet-isolated and v10.0. Both slots return HTTP 200 for the same request.
- Mark
FUNCTIONS_WORKER_RUNTIME as a deployment slot setting in both slots, which the migration guide warns against ("Don't mark FUNCTIONS_WORKER_RUNTIME as a slot setting").
- Swap staging into production. The swap reports success and takes about 77 seconds.
- Production now runs the isolated payload while
FUNCTIONS_WORKER_RUNTIME stayed at dotnet. Everything else moved with the content: netFrameworkVersion, the 64-bit setting, WEBSITE_RUN_FROM_PACKAGE.
One swap produces both mismatch pairings at once, one per slot:
| Deployed payload |
FUNCTIONS_WORKER_RUNTIME |
netFrameworkVersion |
Host extension loaded |
Result |
| in-process |
dotnet-isolated |
v8.0 |
Functions\4.1053.200 |
HTTP 200, app keeps serving |
| isolated |
dotnet |
v10.0 |
FunctionsInProc\4.641.300 |
HTTP 503, host restart loop |
A swap back restored both slots to HTTP 200.
Expected behavior
The mismatch is reported the way AZFD0013 reports it in the opposite pairing: a diagnostic event that names FUNCTIONS_WORKER_RUNTIME and the detected payload, so the setting is the first thing the operator looks at.
Actual behavior
The AZFD0013 code path doesn't run for this pairing, as the next section shows. What happens instead: the in-process host fails during external startup, restarts every 5 to 20 seconds, and the app returns HTTP 503. What Application Insights recorded:
Microsoft.Azure.WebJobs.Script.ExternalStartupException
Error configuring services in an external startup class.
---> System.IO.FileNotFoundException
Could not load file or assembly 'System.ComponentModel, Version=8.0.0.0, ...'.
Assembly: Microsoft.Azure.WebJobs.Extensions.Storage.Queues, Version=5.3.8.0
Nothing in it names FUNCTIONS_WORKER_RUNTIME, a slot or a payload type, so it reads like a broken package. LogFiles/Application/Functions/Host/ gets no new file, because the host never gets far enough to write its own log. The event log shows the switch of host extension within twelve seconds of the swap:
18:08:44 SiteExtensions\Functions\4.1053.200 started (staging, dotnet-isolated)
18:09:25 <app> shutdown (swap)
18:09:37 SiteExtensions\FunctionsInProc\4.641.300 started (with production settings, dotnet)
Known workarounds
Set FUNCTIONS_WORKER_RUNTIME in the affected slot to the value that matches the deployed payload, or swap back. Both bring the app up again within a restart. To avoid the state in the first place, don't mark FUNCTIONS_WORKER_RUNTIME as a deployment slot setting, as the migration guide says. None of this helps with finding the cause, which is the point of this report: the log gives you an assembly name, not a setting.
Why the host doesn't report it
ScriptHost.ValidateAndLogRuntimeMismatch compares the setting with the payload metadata and logs the diagnostic event under WorkerRuntimeDoesNotMatchWithFunctionMetadataErrorCode, which is AZFD0013. GetFunctionDescriptorsAsync calls it only when the setting is dotnet-isolated:
// this dotnet isolated specific logic is temporary to ensure in-proc payload compatibility with "dotnet-isolated" as the FUNCTIONS_WORKER_RUNTIME value.
if (!_environment.IsLogicApp() && string.Equals(workerRuntime, RpcWorkerConstants.DotNetIsolatedLanguageWorkerName, StringComparison.OrdinalIgnoreCase) && !_environment.IsPlaceholderModeEnabled())
The in-process host that the platform loaded here, v4.641.300, has the same guard. The warning text, "The application will continue to run, but may throw an exception in the future", is therefore accurate where it is emitted. When the hosting configuration enables WorkerRuntimeStrictValidationEnabled (WORKER_RUNTIME_STRICT_VALIDATION_ENABLED, default false), the same check logs an error and throws HostInitializationException instead, which is presumably what separates "Warning" from "Error" in the severity row of the AZFD0013 article.
For dotnet with an isolated payload the current code path never calls this check, so there is no later point at which AZFD0013 would be emitted for this pairing. In this reproduction, startup also ended before function metadata was processed at all: the exception above shows the in-process host failing while loading an extension that comes from the isolated payload (Storage.Queues 5.3.8, where the in-process app pinned 5.3.0). So in this reproduction, the one diagnostic that exists for this mismatch could not fire in the direction that takes the app down. I didn't query the production slot for AZFD0013 at the time, and the resources no longer exist, so this rests on the code path rather than on telemetry.
The same exception text is the subject of #10391, closed in March 2025 with FUNCTIONS_INPROC_NET8_ENABLED as the answer for in-process .NET 8 apps. A later comment there comes from someone migrating to the isolated model who still sees the error. Whether that was this pairing I can't tell from the thread, but it shows where people land when they search for the message.
Suggested change
When FUNCTIONS_WORKER_RUNTIME is dotnet and the payload is an isolated worker payload, report AZFD0013 as an error that names the setting and the detected payload, before external startup tries to load the payload's extensions. The payload may be identifiable before extension loading from artifacts already present in wwwroot, such as worker.config.json or the .azurefunctions/ folder. You'll know which signal is reliable at that point, and where the check belongs.
I realize the in-process model reaches end of support on 10 November 2026, and a change to the in-process host may not be worth making this late. If that's the case, two smaller changes would still help people migrating right now. One is to surface the configured FUNCTIONS_WORKER_RUNTIME value in the failure that reaches the user when an in-process host fails on an isolated payload, so that the setting appears somewhere in the log at all. The other is to name the direction in the existing warning ("an in-process payload under dotnet-isolated"), so that "will continue to run" isn't read as covering the reverse pairing. That leaves the behavior as it is and changes only the sentence people quote.
Once the in-process model is out of support, is FUNCTIONS_WORKER_RUNTIME=dotnet with an isolated payload still expected to reach the in-process host, or is that combination expected to be rejected earlier? If it's rejected, this fixes itself and only the documentation needs the correction.
The AZFD0013 article describes only the first direction as well. MicrosoftDocs/azure-docs doesn't take issues, so I can follow up with a docs PR there once it's clear how the host is meant to behave.
Related information
- Programming language: C#. Production ran the in-process model on .NET 8, staging the isolated worker model on .NET 10 with
Microsoft.Azure.Functions.Worker 2.52.0.
- Bindings used: HTTP trigger and a queue output binding. The extension that fails to load is
Microsoft.Azure.WebJobs.Extensions.Storage.Queues 5.3.8 from the isolated payload, against 5.3.0 pinned in the in-process app.
- Links to source:
ScriptHost.ValidateAndLogRuntimeMismatch and its call site in GetFunctionDescriptorsAsync, plus FunctionsHostingConfigOptions.WorkerRuntimeStrictValidationEnabled, all linked in the section above at dev commit 47c5606.
- Docs: the AZFD0013 article and the slot section of the migration guide.
- Related issue: #10391, same exception text, different cause.
Scope
One application, Windows, one region, one swap plus a swap back. The host extension was FunctionsInProc rather than FunctionsInProc8, because FUNCTIONS_INPROC_NET8_ENABLED was not a slot setting and moved with the swap. Not checked: FunctionsInProc8, Linux, Flex Consumption. The source reading refers to dev at 47c5606 (September 15, 2026) and the tag v4.641.300.
This is about the host's own startup validation path, the one that emits AZFD0013, not about deployment or slot mechanics. The state it reproduces from is a single configuration mismatch:
FUNCTIONS_WORKER_RUNTIMEset todotnetwhile the deployed payload is an isolated worker payload. The steps below are only how the app arrived in that state.Investigative information
August 25 and 26, 2026. The swap quoted below ran from 18:09:21 to 18:10:38
FUNCTIONS_EXTENSION_VERSION~4. Two host extensions were loaded across the swap,SiteExtensions\Functions\4.1053.200before andSiteExtensions\FunctionsInProc\4.641.300afterThe app and its Application Insights resource have since been deleted, so there is nothing left to look up. The report rests on the host source and on the logs quoted below, which I kept. If a live repro with telemetry would help, I can stand the setup up again: two slots, one payload each, one swap.
One HTTP-triggered function plus a diagnostics endpoint that exists only in the isolated payload.
West Europe
Windows
Repro steps
FUNCTIONS_WORKER_RUNTIME=dotnetandnetFrameworkVersionv8.0. Staging holds an isolated .NET 10 payload onMicrosoft.Azure.Functions.Worker2.52.0 withdotnet-isolatedandv10.0. Both slots return HTTP 200 for the same request.FUNCTIONS_WORKER_RUNTIMEas a deployment slot setting in both slots, which the migration guide warns against ("Don't markFUNCTIONS_WORKER_RUNTIMEas a slot setting").FUNCTIONS_WORKER_RUNTIMEstayed atdotnet. Everything else moved with the content:netFrameworkVersion, the 64-bit setting,WEBSITE_RUN_FROM_PACKAGE.One swap produces both mismatch pairings at once, one per slot:
FUNCTIONS_WORKER_RUNTIMEnetFrameworkVersiondotnet-isolatedv8.0Functions\4.1053.200dotnetv10.0FunctionsInProc\4.641.300A swap back restored both slots to HTTP 200.
Expected behavior
The mismatch is reported the way AZFD0013 reports it in the opposite pairing: a diagnostic event that names
FUNCTIONS_WORKER_RUNTIMEand the detected payload, so the setting is the first thing the operator looks at.Actual behavior
The AZFD0013 code path doesn't run for this pairing, as the next section shows. What happens instead: the in-process host fails during external startup, restarts every 5 to 20 seconds, and the app returns HTTP 503. What Application Insights recorded:
Nothing in it names
FUNCTIONS_WORKER_RUNTIME, a slot or a payload type, so it reads like a broken package.LogFiles/Application/Functions/Host/gets no new file, because the host never gets far enough to write its own log. The event log shows the switch of host extension within twelve seconds of the swap:Known workarounds
Set
FUNCTIONS_WORKER_RUNTIMEin the affected slot to the value that matches the deployed payload, or swap back. Both bring the app up again within a restart. To avoid the state in the first place, don't markFUNCTIONS_WORKER_RUNTIMEas a deployment slot setting, as the migration guide says. None of this helps with finding the cause, which is the point of this report: the log gives you an assembly name, not a setting.Why the host doesn't report it
ScriptHost.ValidateAndLogRuntimeMismatchcompares the setting with the payload metadata and logs the diagnostic event underWorkerRuntimeDoesNotMatchWithFunctionMetadataErrorCode, which isAZFD0013.GetFunctionDescriptorsAsynccalls it only when the setting isdotnet-isolated:The in-process host that the platform loaded here,
v4.641.300, has the same guard. The warning text, "The application will continue to run, but may throw an exception in the future", is therefore accurate where it is emitted. When the hosting configuration enablesWorkerRuntimeStrictValidationEnabled(WORKER_RUNTIME_STRICT_VALIDATION_ENABLED, defaultfalse), the same check logs an error and throwsHostInitializationExceptioninstead, which is presumably what separates "Warning" from "Error" in the severity row of the AZFD0013 article.For
dotnetwith an isolated payload the current code path never calls this check, so there is no later point at which AZFD0013 would be emitted for this pairing. In this reproduction, startup also ended before function metadata was processed at all: the exception above shows the in-process host failing while loading an extension that comes from the isolated payload (Storage.Queues5.3.8, where the in-process app pinned 5.3.0). So in this reproduction, the one diagnostic that exists for this mismatch could not fire in the direction that takes the app down. I didn't query the production slot for AZFD0013 at the time, and the resources no longer exist, so this rests on the code path rather than on telemetry.The same exception text is the subject of #10391, closed in March 2025 with
FUNCTIONS_INPROC_NET8_ENABLEDas the answer for in-process .NET 8 apps. A later comment there comes from someone migrating to the isolated model who still sees the error. Whether that was this pairing I can't tell from the thread, but it shows where people land when they search for the message.Suggested change
When
FUNCTIONS_WORKER_RUNTIMEisdotnetand the payload is an isolated worker payload, report AZFD0013 as an error that names the setting and the detected payload, before external startup tries to load the payload's extensions. The payload may be identifiable before extension loading from artifacts already present inwwwroot, such asworker.config.jsonor the.azurefunctions/folder. You'll know which signal is reliable at that point, and where the check belongs.I realize the in-process model reaches end of support on 10 November 2026, and a change to the in-process host may not be worth making this late. If that's the case, two smaller changes would still help people migrating right now. One is to surface the configured
FUNCTIONS_WORKER_RUNTIMEvalue in the failure that reaches the user when an in-process host fails on an isolated payload, so that the setting appears somewhere in the log at all. The other is to name the direction in the existing warning ("an in-process payload underdotnet-isolated"), so that "will continue to run" isn't read as covering the reverse pairing. That leaves the behavior as it is and changes only the sentence people quote.Once the in-process model is out of support, is FUNCTIONS_WORKER_RUNTIME=dotnet with an isolated payload still expected to reach the in-process host, or is that combination expected to be rejected earlier? If it's rejected, this fixes itself and only the documentation needs the correction.
The AZFD0013 article describes only the first direction as well.
MicrosoftDocs/azure-docsdoesn't take issues, so I can follow up with a docs PR there once it's clear how the host is meant to behave.Related information
Microsoft.Azure.Functions.Worker2.52.0.Microsoft.Azure.WebJobs.Extensions.Storage.Queues5.3.8 from the isolated payload, against 5.3.0 pinned in the in-process app.ScriptHost.ValidateAndLogRuntimeMismatchand its call site inGetFunctionDescriptorsAsync, plusFunctionsHostingConfigOptions.WorkerRuntimeStrictValidationEnabled, all linked in the section above atdevcommit47c5606.Scope
One application, Windows, one region, one swap plus a swap back. The host extension was
FunctionsInProcrather thanFunctionsInProc8, becauseFUNCTIONS_INPROC_NET8_ENABLEDwas not a slot setting and moved with the swap. Not checked:FunctionsInProc8, Linux, Flex Consumption. The source reading refers todevat47c5606(September 15, 2026) and the tagv4.641.300.