Output Routing
After a node completes, the execution engine needs to know which nodes to execute next. This is determined by the node's output port key and the connection edges defined in the thread definition.
How Routing Works
Every NodeExecutionResult carries an OutputPortKey — a string
that names the logical output the node is emitting from. Common port keys are
"success" and "error", but nodes can define any ports they need.
The IExecutionRouter.GetDownstreamNodesForOutputPort method takes the
current element definition, the port key, and the thread definition, then returns
the list of downstream ProcessElementDefinition objects connected to
that port. These are pushed onto the execution stack.
Routing Decision Flow
Standard Port Keys
| Port Key | Meaning | Special behaviour |
|---|---|---|
"success" | Normal completion | Standard downstream routing |
"error" | Node reported failure | Routes to error-handler nodes if connected; otherwise thread may fail |
"true" / "false" | Boolean condition result | Used by IF/condition nodes |
"waiting" | Node suspended for human input | Triggers durable suspension — no downstream routing |
"pending" | Node suspended for form/event | Triggers durable suspension — no downstream routing |
Special Control Flow Signals
Some routing decisions bypass the port key entirely and use flags set directly on ExecutionMemory:
Loop Control
When a loop-break or loop-continue node executes, it sets
Memory.LoopBreakSignal or Memory.LoopContinueSignal.
The output routing step checks these flags before consulting the port key.
When set, normal downstream routing is skipped entirely and the loop orchestrator
handles the iteration logic.
Abort Signal
A WorkflowControl node can call Memory.SignalAbort(). The thread
loop checks Memory.AbortSignal after each node. When set, the loop
exits and the thread transitions to Cancelled.
Parallel Routing
When Memory.IsParallelExecutionActive is true, routing works differently:
instead of pushing downstream nodes onto the main stack, they are pushed onto the
active parallel lane via _parallelExecutionManager.PushNodeToActiveLane.
A parallel join node waits until all lanes have completed, then merges outputs and resumes normal sequential routing.
What Happens with No Connections
If a node's output port has no downstream connections, GetDownstreamNodesForOutputPort
returns an empty list. Nothing is pushed onto the stack. The execution loop continues
and eventually exhausts the stack, ending the thread normally with Completed.
This is the normal way a terminal node (like "Send Email" at the end of a flow) ends execution — not by throwing or returning a special state, but simply by having no outgoing edges.