# Direct State Control A process instance can get stuck. A command may be unavailable because its condition is no longer met. A transition may reference a deleted action. An error handler may loop without advancing. Without a way to manually reposition the instance, the only option is to delete it and start over. Direct state control lets you move a process instance to a target activity directly, bypassing the normal command execution pipeline. You specify the target state, and the runtime places the instance there without selecting a configured transition into the target. The trigger, conditions, and restrictions of such a transition are not used for the direct jump. With direct state control, stuck processes are recoverable without deleting the instance or editing persistence directly. An operator repositions a failed invoice approval from an error activity back to "Pending Approval" and the process continues. ## What it is Think of direct state control as a manual override on a machine. Normal operation follows a configured route with its checks and steps. The override moves the machine to an approved position directly - useful when the normal controls cannot work because the machine is in an unexpected state. `SetStateAsync` is the runtime API that performs the override. It takes a process instance identifier and a target state name. The runtime can move the process only to an activity whose `State` matches that name and whose `IsForSetState` option is enabled. If no such activity exists, the runtime throws an exception. The method supports two modes controlled by the `PreventExecution` parameter: - With execution (default, `PreventExecution = false`) - the runtime runs the target activity's `Implementation`, including its configured actions, and then processes automatic transitions from that activity normally. Conditions on those automatic transitions are evaluated. - Without execution (`PreventExecution = true`) - the runtime persists the target activity without running its `Implementation` or processing automatic transitions. The process is set to `Idled`, or to `Finalized` when the target is final. The direct jump does not execute the current activity's `Implementation` or a configured transition into the target. With execution, configured process logging can record the set-state execution and the resulting activity and state changes. The without-execution path updates persistence but does not produce the same execution log, so add application-level auditing if the operation must always be recorded. **For developers:** call `WorkflowRuntime.SetStateAsync(SetStateParams)` with the process ID and target state name. In the scheme, give the target activity a `State` and set `IsForSetState = true`. Set `PreventExecution = true` to skip target activity execution. See Set State Directly reference for API details. ## Why it matters Direct state control delivers these outcomes: - **Recover stuck processes without data loss** - a process instance stuck in an error loop or an invalid state can be repositioned to a valid activity. All its parameters and history are preserved - no need to delete and restart. - **Bypass an unavailable route** - the direct jump does not use a command, actor rule, or configured transition into the target. The target still needs a matching `State` and `IsForSetState = true`. With execution enabled, conditions on automatic transitions from the target are processed normally. - **Use the runtime instead of editing the database** - modifying the current activity in the database directly bypasses runtime processing. `SetStateAsync` validates the target, updates persistence through the runtime, and leaves the process `Idled` or `Finalized` when execution finishes at that activity. - **Two modes for different recovery scenarios** - with execution lets the process auto-advance after the state change. Without execution lets the operator place the process precisely and decide the next step manually. ## Who it is for **Developer:** `SetStateAsync` is a single API call. The target activity must be marked with `IsForSetState = true` in the scheme - you decide which activities are safe to jump to. The method does not replace `ExecuteCommandAsync` for normal flow; it is an escape hatch for cases where normal execution cannot apply. See Set State Directly reference for the API reference. **Enterprise architect:** the direct jump does not perform the actor authorization used for command execution. Your application must restrict access to this API at the integration layer. When execution is enabled, the target activity's actions run and conditions on its automatic transitions are evaluated normally. ## When to use it Use direct state control for interventions that cannot be accomplished through normal command execution. Typical examples: - **Recovering stuck instances** - a support ticket workflow encountered an error during a web service call and the error handling activity has no outgoing command. An operator uses `SetStateAsync` to move the instance back to an earlier activity and retry. - **Applying state migrations after scheme changes** - a scheme update removed a transition path that running instances depended on. Use `SetStateAsync` in a migration script to reposition affected instances to compatible activities. - **Admin troubleshooting tools** - build an admin panel that lets support staff move instances between states for testing or diagnosis. The `IsForSetState` flag on activities limits which states are reachable. Do not use `SetStateAsync` as a substitute for `ExecuteCommandAsync` in normal process flow. Reserve it for cases where the normal command pipeline cannot apply. ## How it compares The alternatives to `SetStateAsync` for dealing with stuck processes are destructive or risky. The table below shows the tradeoffs. Table: Comparison of stuck process recovery approaches | Approach | Risk | Result | |-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| | **Delete and restart** | Loses all process parameters, history, and context accumulated in the stuck instance. | Data loss. User must re-enter information. | | **Direct database update** | Skips all runtime validation - may leave the process in an inconsistent state with orphaned timers, wrong status, or broken parameter state. | Runtime consistency can be lost. | | **Workflow Engine `SetStateAsync`** | Validates the target activity and updates persistence through the runtime. Target actions may have side effects unless execution is prevented. | The process keeps its parameters and existing history. Add application-level auditing if required. | ## Frequently asked questions **When should I use SetStateAsync instead of ExecuteCommandAsync?** Use `ExecuteCommandAsync` for normal process progression where conditions, permissions, and actions should be evaluated. Use `SetStateAsync` only for manual overrides - recovering stuck instances, migrating process state after a scheme change, or building tooling that needs to reposition instances outside the normal flow. **Do I need to set IsForSetState on every activity I want to jump to?** Yes. A target activity must have `IsForSetState = true` and a non-empty `State` matching the requested state name. Attempting to set an unavailable state causes the runtime to throw `ActivityNotFoundException`. The target is identified by its state name, not its activity name. **What is the difference between SetStateAsync with and without execution?** With execution (default), the runtime runs the target activity's `Implementation` and then processes automatic transitions from it. Without execution (`PreventExecution = true`), the runtime persists the target without running its `Implementation` or automatic transitions, then sets the process to `Idled` or `Finalized`. **Which activity actions does SetStateAsync execute?** It does not run the current activity's `Implementation`. With `PreventExecution = false`, it runs the target activity's `Implementation` and may continue through automatic transitions. With `PreventExecution = true`, it does not run the target activity or process its automatic transitions. **What are the security implications of SetStateAsync?** The direct jump does not perform the actor authorization used by `ExecuteCommandAsync`. Access to this method in your application should be controlled according to your security requirements - the runtime does not validate whether the supplied identity may perform this operation. ## See also - Set State Directly Reference: API reference and detailed usage of WorkflowRuntime.SetStateAsync. - Execute Commands: Normal command execution with conditions, permissions, and actions. - [Timers & Scheduling](https://docs.workflowengine.io/evaluate/workflow-engine-features/timers-scheduling/): Automatic state changes through timers as an alternative to manual intervention.