This document explains the nested workflow system, which allows workflows to execute other workflows as blocks. A workflow block invokes a child workflow with mapped inputs, waits for completion, and returns the child's outputs to the parent workflow. This enables workflow composition, reusability, and modular design patterns.
For information about workflow execution fundamentals, see Workflow Execution Engine. For loop and parallel execution, see Loop & Parallel Execution. For variable resolution across workflows, see Variable Resolution.
The nested workflow system centers on the WorkflowBlockHandler, which implements the BlockHandler interface to manage child workflow execution within a parent workflow context.
Component Diagram
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts1-554 apps/sim/executor/index.ts1-7
The WorkflowBlockHandler class manages the complete lifecycle of child workflow execution.
Class Structure
| Method | Purpose |
|---|---|
canHandle() | Identifies workflow/workflow_input block types |
execute() | Main execution entry point |
loadChildWorkflow() | Fetches draft workflow state |
loadChildWorkflowDeployed() | Fetches deployed workflow state |
checkChildDeployment() | Validates child deployment status |
captureChildWorkflowLogs() | Extracts trace spans from child |
mapChildOutputToParent() | Maps child results to parent format |
buildNestedWorkflowErrorMessage() | Constructs error chains |
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts35-553
The nested workflow execution follows a multi-stage process involving depth checking, workflow loading, input mapping, execution, and output mapping.
Execution Pipeline
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts43-189
The system enforces a maximum nesting depth of 10 levels to prevent infinite recursion and stack overflow errors.
Depth Calculation
The depth is calculated from the workflowId string, which uses the _sub_ separator to track nesting levels:
level1 β depth = 0
level1_sub_level2 β depth = 1
level1_sub_level2_sub_level3 β depth = 2
Implementation:
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts63-66 apps/sim/executor/handlers/workflow/workflow-handler.test.ts110-123
Child workflows receive inputs through an inputMapping configuration that maps parent outputs to child inputs.
Input Mapping Process
| Stage | Action | Function |
|---|---|---|
| Parse | Convert string/object to normalized structure | parseJSON() |
| Clean | Remove invalid references, validate against child blocks | lazyCleanupInputMapping() |
| Apply | Pass cleaned mapping as workflowInput to executor | Constructor parameter |
Mapping Structures:
The lazyCleanupInputMapping() function validates mappings against the child workflow's block definitions to ensure referenced fields exist.
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts92-110
Child workflows can execute in either draft mode or deployed mode, depending on the parent's execution context.
Context Resolution
When executing in deployed context, the handler enforces that child workflows must also be deployed. This ensures consistency in production environments.
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts68-75 apps/sim/executor/handlers/workflow/workflow-handler.ts313-389
The handler loads child workflow state through two different paths depending on the execution context.
Draft Workflow Loading:
GET /api/workflows/{workflowId}blocks, edges, loops, parallelsSerializer.serializeWorkflow()Deployed Workflow Loading:
GET /api/workflows/{workflowId}/deployedGET /api/workflows/{workflowId} (for variables)deployedState instead of current stateBoth paths use the Serializer to convert the workflow state into the executable format expected by the Executor.
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts257-311 apps/sim/executor/handlers/workflow/workflow-handler.ts333-389
Once the workflow is loaded, a new Executor instance is created for the child workflow with specific context extensions.
Executor Configuration:
Context Extension Properties:
| Property | Purpose |
|---|---|
isChildExecution | Marks this as a nested execution |
isDeployedContext | Propagates deployment mode |
workspaceId | Maintains workspace context |
userId | Preserves user identity |
executionId | Links to parent execution |
abortSignal | Enables cancellation propagation |
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts118-131
Errors in child workflows are captured and propagated to the parent with enhanced error messages that show the workflow chain.
Error Handling Flow
Error Message Patterns:
| Pattern | Example |
|---|---|
| Single level | "Child Workflow" failed: Block Name: error message |
| Nested (chain) | Workflow chain: Parent β Child1 β Child2 | error message |
The buildNestedWorkflowErrorMessage() method parses error messages to extract workflow chains and preserve the root error cause.
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts156-188 apps/sim/executor/handlers/workflow/workflow-handler.ts195-255
The ChildWorkflowError class encapsulates all information about a failed child workflow execution.
Error Structure:
This structured error allows the parent workflow to:
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts8 apps/sim/executor/handlers/workflow/workflow-handler.ts180-187
Child workflow execution logs are captured as trace spans and attached to the parent workflow's execution logs.
Trace Span Capture Process
Trace Span Transformation:
Each child trace span is transformed to include metadata marking it as from a child workflow:
Synthetic workflow wrapper spans (spans without a blockId) are removed to flatten the hierarchy and avoid redundant nesting.
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts394-425 apps/sim/executor/handlers/workflow/workflow-handler.ts427-448 apps/sim/executor/handlers/workflow/workflow-handler.ts450-473
Child workflow outputs are mapped to the parent workflow's expected output format.
Output Structure
Mapped Fields:
| Field | Source | Purpose |
|---|---|---|
success | Child success flag | Indicates completion status |
childWorkflowName | Workflow metadata | Display name |
childWorkflowId | Input parameter | Workflow identifier |
childWorkflowSnapshotId | Snapshot service | Link to execution snapshot |
result | Child output | Child workflow's final output |
childTraceSpans | Transformed spans | Execution traces for logging |
If the child workflow fails (success: false), the handler throws a ChildWorkflowError so that the parent's BlockExecutor can check for an error port connection.
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts523-552
Before executing the child workflow, a snapshot is created for debugging and replay purposes.
Snapshot Process:
The snapshotService.createSnapshotWithDeduplication() creates or reuses a snapshot of the child workflow's state. The snapshot ID is:
This enables users to replay child workflow executions from the exact state that was executed.
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts112-116
The handler identifies workflow blocks by checking the block's metadata ID.
Supported Block Types:
| Block Type | Constant | Description |
|---|---|---|
workflow | BlockType.WORKFLOW | Standard workflow block |
workflow_input | BlockType.WORKFLOW_INPUT | Alternative workflow block |
Sources: apps/sim/executor/handlers/workflow/workflow-handler.ts38-41
Certain output keys are hidden from display and logs to keep outputs clean while preserving data for internal processing.
Hidden Keys:
The childTraceSpans field is stored in block logs but filtered from the output display. This allows:
The filterHiddenOutputKeys() function recursively removes these keys from nested objects before display.
Sources: apps/sim/lib/logs/execution/trace-spans/trace-spans.ts12 apps/sim/lib/logs/execution/trace-spans/trace-spans.ts18-39 apps/sim/executor/utils/output-filter.ts1-54
The WorkflowBlockHandler includes comprehensive test coverage for error conditions, depth limiting, and output mapping.
Test Coverage:
| Test Case | File Location | Purpose |
|---|---|---|
canHandle() | workflow-handler.test.ts:90-99 | Block type identification |
| Missing workflowId | workflow-handler.test.ts:102-108 | Input validation |
| Depth limit | workflow-handler.test.ts:110-123 | Maximum nesting enforcement |
| Workflow not found | workflow-handler.test.ts:125-137 | 404 error handling |
| Network errors | workflow-handler.test.ts:139-147 | Fetch failure handling |
| Invalid state | workflow-handler.test.ts:165-182 | Malformed workflow data |
| Output mapping | workflow-handler.test.ts:185-244 | Success/failure mapping |
Sources: apps/sim/executor/handlers/workflow/workflow-handler.test.ts1-247
The WorkflowBlockHandler integrates with the broader block execution system through the BlockHandler interface.
Handler Registration:
The workflow handler is registered alongside other block handlers (agent, router, tool, etc.) and is invoked when the BlockExecutor encounters a workflow block.
Execution Delegation:
The BlockExecutor calls canHandle() on each registered handler until it finds one that returns true, then delegates execution to that handler's execute() method.
Sources: apps/sim/executor/types.ts330-351
Child workflow trace spans are integrated into the parent execution's log hierarchy.
Log Integration:
The buildTraceSpans() function processes child trace spans when building the execution trace tree:
This creates a hierarchical trace where child workflow blocks expand to show their internal execution steps, providing full visibility into nested workflow execution.
Sources: apps/sim/lib/logs/execution/trace-spans/trace-spans.ts389-407 apps/sim/lib/logs/execution/trace-spans/trace-spans.ts46-83
Refresh this wiki
This wiki was recently refreshed. Please wait 6 days to refresh again.