Flow options
Dex allows you to customize the behavior of a Flow execution.
- StartFlowOptions apply when a client starts a Flow (or SubFlow). They are immutable after start.
- They control timeout, ID reuse, initial Attributes, request idempotency, and optional config overrides for that execution.
- FlowConfig provide runtime behavior. They are mutable after start.
- Step durability defaults, Worker routing, active-Step search indexing, continue-as-new thresholds, and Attribute Store projection targets.
StartFlowOptions
| Field | Purpose |
|---|---|
| Timeout | Flow timeout. Omit it or set zero to disable it. |
| TimeoutPolicy | What happens when the flow timeout fires. |
| TimeoutHandlerOptions | Execution, retry, recovery, durability, locking, and state loading for a timeout handler. |
| IDReusePolicy | How to reuse existing Flow ID to start |
| RequestID | Idempotency key for retrying one start request. |
| AlreadyStartedOptions | How to handle an already-started Flow. |
| RetryPolicy | Retry policy for one Flow execution after failure. |
| StartDelay | Delay before the start Step to run. |
| Attributes | Initial Attribute values. |
| FlowConfig | Per-execution Flow configuration |
For an application-defined outcome at the Flow deadline, see Graceful Timeout.
def example_start_flow_options() -> StartFlowOptions:
return StartFlowOptions(
timeout=timedelta(minutes=30),
timeout_policy=FlowTimeoutPolicy.HANDLER,
start_delay=timedelta(minutes=5),
id_reuse_policy=IdReusePolicy.DISALLOW,
retry_policy=RetryPolicy(
initial_interval=timedelta(minutes=1),
backoff_coefficient=2,
maximum_interval=timedelta(minutes=10),
maximum_attempts=3,
),
config_override=FlowConfig(step_durability=StepDurability.SYNC),
ignore_already_started=True,
request_id="start-order-123",
).with_attribute(status, "queued")
Example: examples/python/dex_examples/primitives/flow/controller.py
Timeout and TimeoutPolicy
Timeout is one durable timer for the whole Flow. Set TimeoutPolicy only when Timeout is positive.
- FAIL — fail the Flow with a timeout error. RetryPolicy can retry it.
- CANCEL — cancel the Flow. It does not retry.
- HANDLER — run one logical Flow timeout-handler execution.
With a positive Timeout, a Dex SDK uses FAIL by default. If the Flow has a timeout handler, it uses HANDLER instead. HANDLER is invalid when the Flow has no handler. A retry starts with a new timeout budget.
TimeoutHandlerOptions is valid only with a positive Timeout and a final HANDLER policy. It uses Execute semantics: a method timeout limits each attempt, Retry can run multiple attempts, HeartbeatTimeout detects stalled regular attempts, and Failure can route exhausted retries to a registered no-input Step. The recovery Step receives null or unit input and reads the final error from the handler Context.
The handler's attempt and retry clocks begin when the soft Flow timeout fires. They may extend past the original Flow deadline. If no failure target is configured, an exhausted handler fails the Flow.
Ordinary Attributes and all Channel size metadata load automatically. AttributeMap values and pending Channel messages require explicit selections in TimeoutHandlerOptions. The handler may stage Attribute writes, Channel deletions, and Channel publications before returning its decision. Continue-as-new and Flow retries preserve the options; a Flow retry starts a new soft-timeout budget.
Set TimeoutPolicy to HANDLER to run this code when the timeout fires. This handler records why the Flow ended, then fails it with an application reason.
def handle_timeout(self, context: Context) -> StepDecision:
status.set(context, "timed out")
return force_fail("processing deadline reached")
Example: examples/python/dex_examples/primitives/flow/example_flow.py
Flow ID, RequestID, and AlreadyStartedOptions
IDReusePolicy says what to do when this Flow ID already exists. RequestID says whether this call retries the same start request.
How these options work together
These options answer different questions, in this order:
- RequestID identifies one start request. When omitted, the SDK generates a new UUID, so another StartFlow call is a new request. Reuse it only for a retry of the exact same start. Dex compares the RequestID, not the start input or options.
- IDReusePolicy decides whether that request may start another run with this Flow ID.
- AlreadyStartedOptions.IgnoreError matters only when the ID reuse policy rejects the start as already started. It returns the existing Run ID only when the RequestID matches.
| Retry request | AlreadyStartedOptions.IgnoreError | When the ID reuse policy rejects the start |
|---|---|---|
| No RequestID, or a new RequestID | Off or on | The SDK sends a new UUID. Dex returns an already-started error. |
| Same RequestID | Off | Dex returns an already-started error. |
| Same RequestID | On | Dex returns the existing Run ID. |
The ID reuse policy determines whether Dex rejects the start in the first place:
| IDReusePolicy | Existing active Flow | Existing closed Flow |
|---|---|---|
| IDReuseDefault | Rejects the start. A matching RequestID with IgnoreError returns the active Run ID. | Starts a new run. |
| IDReuseAllowIfPreviousFailed | Rejects the start. A matching RequestID with IgnoreError returns the active Run ID. | Starts a new run only after a failed run; otherwise rejects it. |
| IDReuseAllowIfNotRunning | Rejects the start. A matching RequestID with IgnoreError returns the active Run ID. | Starts a new run. |
| IDReuseDisallow | Rejects the start. A matching RequestID with IgnoreError returns the existing Run ID. | Rejects the start. A matching RequestID with IgnoreError returns the existing Run ID. |
| IDReuseTerminateIfRunning | Terminates the active run and starts a replacement. It does not produce an already-started error. | Starts a new run. |
What Dex Server does
Dex Server requires a non-empty RequestID. An SDK generates one before sending the request when you omit it. The server records it with the Flow execution, then asks the configured backend to start the Flow using the requested ID reuse policy. IDReuseDefault uses the server default: start a new Flow execution when no execution is active.
If the backend reports that the Flow is already started, Dex checks AlreadyStartedOptions.IgnoreError. When it is on, Dex reads the existing run's recorded RequestID. It returns that Run ID only when the two RequestIDs match. Otherwise it returns the already-started error.
RetryPolicy
RetryPolicy retries one Flow execution after it fails. MaximumAttempts includes the first execution. InitialInterval, BackoffCoefficient, and MaximumInterval set the exponential backoff. A FAIL timeout can use this retry policy. A CANCEL timeout cannot.
StartDelay
StartDelay accepts the Flow to start now, but waits before the StartStep can run. Use it for a one-off delay, for example, wait five minutes before processing a new order. Use a durable Timer loop for recurring work.
Attributes
Attributes write initial values . Each value must match an Attribute or Attribute map in the Flow persistence schema.
FlowConfig
Use ConfigOverride in StartFlowOptions to change the initial configuration for one Flow. It changes only the fields you provide. It does not replace the whole FlowConfig.
At start, Dex applies the supplied fields over the Dex Server defaults. Omitted fields keep their default values.
| Field | Behavior |
|---|---|
| ActiveStepSearchMode | Index every active Step, only Steps that ran WaitFor, or no active Steps. The default indexes Steps with WaitFor. |
| ContinueAsNewThreshold | Continue as new after this many tracked events. Zero turns it off. |
| ContinueAsNewPageSizeBytes | Limit each page of state carried into continue-as-new. Zero uses 1 MiB. |
| StepDurability | Default durability for methods without a Step override. Dex resolves a method override first, then this value, then SYNC. |
| WorkerTarget | WorkerService endpoint for Step and RPC invocation |
| AttributeStoreNames | Dex Server Attribute Stores for Attributes that opt in to attribute sync |
Update FlowConfig with a Client
Call UpdateFlowConfig while the Flow is active. Send only the fields you want to change.They will be merged with the existing configuration.