Problem Description
We are running Durable Functions orchestrations on Azure Functions Isolated Worker (.NET 10, Premium plans, Linux). When the Azure Functions platform performs a scale-in (removes a worker instance), in-flight orchestrator functions throw System.ObjectDisposedException: Cannot access a disposed object. Object name: 'IServiceProvider' and are permanently marked as RuntimeStatus = Failed. The Durable Task framework does not retry these orchestrations — they remain in Failed state indefinitely, causing upstream workflows to become stuck.
The cascade
When the host initiates shutdown for scale-in, the DI ServiceProvider is disposed. However, orchestrator functions that have already been dispatched (messages dequeued from the control queue and handed to the worker) attempt to resolve services via DefaultFunctionContext.get_InstanceServices() which calls ServiceProvider.CreateScope(). This throws ObjectDisposedException because the container is already disposed.
The Durable Task extension treats this as an unhandled orchestrator failure and commits RuntimeStatus = Failed to the Instances table. The failure then propagates up the orchestration call chain:
wait-for-task (sub-orchestration) — fails first with ObjectDisposedException
migrate-site-workflow (parent) — receives SubOrchestrationInstanceFailed from wait-for-task and also transitions to Failed
process-one-backlog-item-workflow (top-level) — receives the cascading failure and permanently fails
Inconsistent retry behavior: In some cases, the framework appears to retry the failed orchestration — the queue message is picked up by another instance and the workflow recovers. However, in other cases (the majority we observe), the failure is permanent: RuntimeStatus = Failed is committed to the Instances table and no retry occurs. The business workflow is permanently abandoned with no automatic recovery.
The critical issue: Unlike activity function failures which leave queue messages available for retry, orchestrator failures sometimes commit a terminal Failed status to the instance store. Once committed, the orchestration is permanently dead — no re-dispatch. The non-deterministic nature of retry vs permanent failure makes this particularly dangerous: there is no reliable way to predict which orchestrations will recover and which will be permanently lost.
Observed timeline (real production incident, 2026-06-01 13:45 UTC)
| Time (UTC) |
Event |
| 13:45:23.834 |
Host logs: "Application is shutting down..." on instance 0--bb3e733a... |
| 13:45:24.015 |
29 orchestrations fetch TimerFired messages from control queues |
| 13:45:24.051 |
Orchestrations begin processing (timer delays 63–64 seconds) |
| 13:45:24.098 |
ObjectDisposedException ×28 thrown — ServiceProvider already disposed |
| 13:45:24.155 |
wait-for-task orchestrations set to RuntimeStatus: Failed |
| 13:45:24.165 |
SubOrchestrationInstanceFailed messages sent to parent orchestrations |
| 13:45:24.197 |
Instance tables updated: 29 orchestrations permanently Failed |
| 13:45:28.445 |
Parent process-one-backlog-item-workflow orchestrations also Failed |
After shutdown, the instance never appeared again — confirming this was a permanent scale-in, not a restart.
Key observations
-
The ServiceProvider is disposed before in-flight invocations complete. The shutdown signal and ServiceProvider disposal happen within 28–478ms of each other. Orchestrator functions already dispatched cannot resolve dependencies.
-
Orchestrator failures are permanent — no retry. Unlike activity failures (which return the message to the queue), when an orchestrator function fails, the extension commits RuntimeStatus = Failed to the Instances table. The Durable Task framework has no built-in retry for orchestrator failures.
-
One scale-in event kills many orchestrations. In the worst observed incident, a single instance shutdown caused 29 sub-orchestrations and their parent workflows to fail permanently. With many orchestrations sharing a single instance, one scale-in can cascade into dozens of permanently abandoned workflows.
-
SubOrchestrationInstanceFailed propagates fatally. When a child wait-for-task orchestration fails, the parent process-one-backlog-item-workflow receives SubOrchestrationInstanceFailed and also transitions to Failed — extending the blast radius beyond the originally affected instance.
-
Contrast: Timer-based orchestrations survive. Orchestrations that were merely waiting on timers (not actively executing) survive because their timer messages remain on the control queue and are picked up by a new instance. Only orchestrations whose code was actively executing at shutdown time are permanently lost.
Impact (3-day observation window, EU region only)
| Metric |
Value |
Total ObjectDisposedException occurrences |
169 |
| Unique worker instances affected (scale-in events) |
7 |
| Permanently failed orchestration instances |
39 |
| Affected function apps |
odmt-eu-b1-files-workers, odmt-eu-b1-mail-workers |
| Failed orchestration types |
wait-for-task, process-one-backlog-item-workflow |
| Customer impact |
Migration tasks stuck permanently — no completion |
Extension log confirming permanent failure
739294d1f4be5a8ab7541cc46f088164: Function 'wait-for-task (Orchestrator)' failed with an error.
Reason: Microsoft.Azure.WebJobs.Host.FunctionInvocationException
IsReplay: False. State: Failed. RuntimeStatus: Failed.
HubName: filesworkers. AppName: odmt-eu-b1-files-workers.
SlotName: Production. ExtensionVersion: 3.12.5. SequenceNumber: 16420. TaskEventId: -1
Exception stack trace
Microsoft.Azure.WebJobs.Host.FunctionInvocationException:
Exception while executing function: Functions.process-one-backlog-item-workflow
---> Microsoft.Azure.WebJobs.Script.Workers.Rpc.RpcException:
Result: Failure
Type: System.ObjectDisposedException
Exception: Cannot access a disposed object.
Object name: 'IServiceProvider'.
Stack:
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper
.ThrowObjectDisposedException()
at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateScope()
at Microsoft.Azure.Functions.Worker.DefaultFunctionContext
.get_InstanceServices()
in /*/src/DotNetWorker.Core/Context/DefaultFunctionContext.cs:line 46
at Microsoft.Extensions.Hosting.MiddlewareWorkerApplicationBuilderExtensions
.<>c__DisplayClass3_0`1.b__1(FunctionContext context)
in /_/src/DotNetWorker.Core/Hosting/WorkerMiddlewareWorkerApplicationBuilderExtensions.cs:line 105
at Microsoft.Azure.Functions.Worker.FunctionsApplication
.InvokeFunctionAsync(FunctionContext context)
in /_/src/DotNetWorker.Core/FunctionsApplication.cs:line 83
at Microsoft.Azure.Functions.Worker.Handlers.InvocationHandler
.InvokeAsync(InvocationRequest request)
in /_/src/DotNetWorker.Grpc/Handlers/InvocationHandler.cs:line 91
Expected behavior
When a Functions host undergoes shutdown/scale-in:
- Pending orchestrator messages should be abandoned (left on queue) so another instance picks them up
- If an orchestrator function invocation fails due to host disposal, the failure should be treated as retriable (not permanently terminal)
- The
ServiceProvider disposal should be deferred until all in-flight function invocations complete (graceful drain)
Product: Durable Azure Functions (Isolated Worker, .NET 10)
Plan: Flex Consumption
Durable Task Extension: Microsoft.Azure.Functions.Worker.Extensions.DurableTask 1.16.5
Task Hub: filesworkers, mailboxworkers
OS: Linux
Regions affected: North Europe
Problem Description
We are running Durable Functions orchestrations on Azure Functions Isolated Worker (.NET 10, Premium plans, Linux). When the Azure Functions platform performs a scale-in (removes a worker instance), in-flight orchestrator functions throw
System.ObjectDisposedException: Cannot access a disposed object. Object name: 'IServiceProvider'and are permanently marked asRuntimeStatus = Failed. The Durable Task framework does not retry these orchestrations — they remain inFailedstate indefinitely, causing upstream workflows to become stuck.The cascade
When the host initiates shutdown for scale-in, the DI
ServiceProvideris disposed. However, orchestrator functions that have already been dispatched (messages dequeued from the control queue and handed to the worker) attempt to resolve services viaDefaultFunctionContext.get_InstanceServices()which callsServiceProvider.CreateScope(). This throwsObjectDisposedExceptionbecause the container is already disposed.The Durable Task extension treats this as an unhandled orchestrator failure and commits
RuntimeStatus = Failedto the Instances table. The failure then propagates up the orchestration call chain:wait-for-task(sub-orchestration) — fails first withObjectDisposedExceptionmigrate-site-workflow(parent) — receivesSubOrchestrationInstanceFailedfromwait-for-taskand also transitions toFailedprocess-one-backlog-item-workflow(top-level) — receives the cascading failure and permanently failsInconsistent retry behavior: In some cases, the framework appears to retry the failed orchestration — the queue message is picked up by another instance and the workflow recovers. However, in other cases (the majority we observe), the failure is permanent:
RuntimeStatus = Failedis committed to the Instances table and no retry occurs. The business workflow is permanently abandoned with no automatic recovery.The critical issue: Unlike activity function failures which leave queue messages available for retry, orchestrator failures sometimes commit a terminal
Failedstatus to the instance store. Once committed, the orchestration is permanently dead — no re-dispatch. The non-deterministic nature of retry vs permanent failure makes this particularly dangerous: there is no reliable way to predict which orchestrations will recover and which will be permanently lost.Observed timeline (real production incident, 2026-06-01 13:45 UTC)
0--bb3e733a...TimerFiredmessages from control queuesObjectDisposedException×28 thrown —ServiceProvideralready disposedwait-for-taskorchestrations set toRuntimeStatus: FailedSubOrchestrationInstanceFailedmessages sent to parent orchestrationsFailedprocess-one-backlog-item-workfloworchestrations alsoFailedAfter shutdown, the instance never appeared again — confirming this was a permanent scale-in, not a restart.
Key observations
The ServiceProvider is disposed before in-flight invocations complete. The shutdown signal and ServiceProvider disposal happen within 28–478ms of each other. Orchestrator functions already dispatched cannot resolve dependencies.
Orchestrator failures are permanent — no retry. Unlike activity failures (which return the message to the queue), when an orchestrator function fails, the extension commits
RuntimeStatus = Failedto the Instances table. The Durable Task framework has no built-in retry for orchestrator failures.One scale-in event kills many orchestrations. In the worst observed incident, a single instance shutdown caused 29 sub-orchestrations and their parent workflows to fail permanently. With many orchestrations sharing a single instance, one scale-in can cascade into dozens of permanently abandoned workflows.
SubOrchestrationInstanceFailed propagates fatally. When a child
wait-for-taskorchestration fails, the parentprocess-one-backlog-item-workflowreceivesSubOrchestrationInstanceFailedand also transitions toFailed— extending the blast radius beyond the originally affected instance.Contrast: Timer-based orchestrations survive. Orchestrations that were merely waiting on timers (not actively executing) survive because their timer messages remain on the control queue and are picked up by a new instance. Only orchestrations whose code was actively executing at shutdown time are permanently lost.
Impact (3-day observation window, EU region only)
ObjectDisposedExceptionoccurrencesodmt-eu-b1-files-workers,odmt-eu-b1-mail-workerswait-for-task,process-one-backlog-item-workflowExtension log confirming permanent failure
Exception stack trace
Expected behavior
When a Functions host undergoes shutdown/scale-in:
ServiceProviderdisposal should be deferred until all in-flight function invocations complete (graceful drain)Product: Durable Azure Functions (Isolated Worker, .NET 10)
Plan: Flex Consumption
Durable Task Extension:
Microsoft.Azure.Functions.Worker.Extensions.DurableTask 1.16.5Task Hub:
filesworkers,mailboxworkersOS: Linux
Regions affected: North Europe