Share via

Azure Durable Functions — Orchestration re-triggers for the same input after completing successfully

BEJJANKI SHREYAS 0 Reputation points
2026-03-19T11:40:01.19+00:00

Body:

Environment:

  • Azure Functions v4 (Python v2 programming model)
  • Durable Functions Extension Bundle [4.*, 5.0.0)
  • [azure-functions-durable] Python SDK
  • Storage Provider: Azure Storage (default)

Scenario:

We have an HTTP-triggered Durable Function that starts an orchestration for a given [fileId]. The orchestration calls a single activity function that performs long-running work (reading data from blob, calling external APIs, uploading documents — can take 15–40 minutes per file).

Observed Behavior:

After the orchestration completes successfully for a [fileId], we see in the logs that the same file gets picked up and processed again — essentially a second execution for an identical input that already finished.

Questions:

Does the Durable Functions framework automatically retry or replay an orchestration instance after it completes? Under what conditions can a completed orchestration be re-triggered without an explicit new HTTP call?

When [instance_id] is set to None in [client.start_new()], a new random GUID is generated each time. Does this mean the framework has no built-in deduplication — i.e., there is nothing preventing two concurrent orchestration instances from running for the same logical input?

For long-running orchestrations (30+ minutes), can the Durable Task Framework's internal polling or lease renewal cause a replay that looks like a re-trigger? We see the activity function re-executing from scratch, not just an orchestrator replay.

Is there a recommended pattern for ensuring "exactly-once" or "at-most-once" processing in Durable Functions? For example:

  • Should we always use a deterministic instance ID (e.g., [FileProcess-{fileId}]) so the framework rejects duplicates?
    • Is [client.get_status()] + checking RuntimeStatus before [start_new()] a reliable guard, or is there a race condition?
      • Does the framework provide a built-in singleton/locking pattern for this?
  1. Does the functionTimeout setting of "-1" (unlimited) have any side effects on orchestration lifecycle or activity execution that could cause unexpected re-triggers?
  2. Could the notify callback (an HTTP GET fired at the end of each batch to the same Function App) inadvertently re-trigger the orchestration? If the notify URL points back to the /process endpoint, would that start a fresh orchestration for the same [fileId]?
Azure Functions
Azure Functions

An Azure service that provides an event-driven serverless compute platform.


1 answer

Sort by: Most helpful
  1. Rakesh Mishra 6,990 Reputation points Microsoft External Staff Moderator
    2026-03-19T15:28:45.5566667+00:00

    Hello Shreyas,

    The behavior you are seeing is almost certainly caused by an external re-trigger rather than the Durable Functions framework itself. The framework does not "ghost-retry" or automatically restart an orchestration once it has successfully reached the Completed state.

    Here is a breakdown of why this happens and how to resolve it:

    1. No Built-in Deduplication for Random GUIDs When you call client.start_new() with instance_id=None, the framework generates a brand-new GUID. It has no awareness of your underlying business logic or fileId. Consequently, if your HTTP endpoint is called twice for the same file, two distinct orchestrations will run concurrently.
      Reference: Instance management in Durable Functions
    2. Activity Function Re-execution vs. Replays
      Durable Functions use event sourcing to rebuild state. During a replay, the framework checks the execution history; if an activity function completed previously, its result is returned from memory, and it is not re-executed. If you see the activity function running from scratch, it guarantees a completely new orchestration instance was initiated.
      Reference: Orchestrator function code constraints
    3. The Notify Callback is Likely the Culprit
      If your batch-complete callback issues an HTTP request that routes back to the same /process endpoint (or another endpoint that invokes start_new), it will spin up a fresh orchestration.
    4. Implementing "Exactly-Once" Processing
      To prevent duplicates, use a deterministic instance ID (e.g., FileProcess-{fileId}). When you pass a predictable ID to start_new(), the framework will return an HTTP 409 (Conflict) if an instance with that ID is already running or completed. Checking client.get_status() before starting is helpful, but relying on deterministic IDs offloads the concurrency lock directly to the Azure Storage provider, avoiding race conditions.
      Reference: Exactly-once and at-most-once processing
    5. functionTimeout Behavior
      The functionTimeout setting in host.json applies to the maximum execution time of individual function invocations (which can be unlimited "-1" on Dedicated/Premium plans). It does not govern the broader lifecycle of the orchestration itself and will never cause a completed orchestration to restart.

    Hope this helps clarify the behavior and let me know in comments if any further clarification is needed.

    Note: This response is generated with the help of AI system.

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.