Workflow Container Nodes (Loop & Iteration)#
Loop and Iteration are the two container node types in the Dify workflow editor. They act as sub-graph hosts: child nodes placed inside a container have a parentId pointing to the container and carry container-scoped metadata flags (isInLoop/isInIteration, loop_id/iteration_id). The backend relies on these frontend-set fields to build the container's internal execution graph; missing metadata causes child nodes to be silently excluded from execution (see Known Issue: parentId and Assign Variable below).
Node Insertion Restrictions#
The availableBlocksFilter function in use-available-blocks.ts is the single source of truth for which node types may appear inside a container. When inContainer is true, the following block types are always filtered out:
| Blocked node type | Reason |
|---|---|
BlockEnum.Iteration | No nested containers |
BlockEnum.Loop | No nested containers |
BlockEnum.End | End node is top-level only |
BlockEnum.DataSource | Top-level trigger only |
BlockEnum.KnowledgeBase | Top-level trigger only |
Symmetrically, BlockEnum.LoopEnd is blocked outside containers β it can only appear inside a Loop.
The paste path in use-nodes-interactions.ts enforces the same list via commonNestedDisallowPasteNodes to keep copy-paste consistent with the drag-and-drop UI.
Human Input Support: Runtime support for HumanInput nodes inside Loop and Iteration containers landed in PR #39243, but the frontend workflow editor guards in use-available-blocks.ts (the availableBlocksFilter function) and use-nodes-interactions.ts (the commonNestedDisallowPasteNodes array) continued blocking Human Input from the node availability filter and paste restrictions. PR #40061 removed BlockEnum.HumanInput from both frontend guard lists, completing the feature and allowing users to actually select, connect, and paste Human Input nodes inside containers.
Child Node Metadata#
When a node is added to or pasted into a container, use-nodes-interactions.ts stamps five metadata fields on it:
| Field | Source |
|---|---|
node.parentId | Set to the container node's ID |
node.data.isInLoop / node.data.isInIteration | true when parent is Loop/Iteration |
node.data.loop_id / node.data.iteration_id | Set to the container node's ID |
These same fields are propagated to edges: initialEdges in workflow-init.ts walks each edge's source and target nodes, finds the nearest parent container, and stamps isInIteration, iteration_id, isInLoop, and loop_id onto edge.data, plus sets edge.zIndex to NESTED_ELEMENT_Z_INDEX for correct rendering.
The container node itself gets a _children array: [{ nodeId, nodeType }] entries are pushed on every child insertion and removed on deletion β it is used to drive the iterationOrLoopNodeMap built during initialNodes.
Workflow Initialization: Start Nodes#
preprocessNodesAndEdges in workflow-init.ts runs at graph load time and synthesizes a IterationStart or LoopStart pseudo-node for every container that lacks one. The generated start node is connected via a synthetic edge whose zIndex is NESTED_ELEMENT_Z_INDEX. This ensures the sub-graph always has a well-defined entry point regardless of whether the workflow was created before start nodes were introduced.
Pasting Nodes Across Container Boundaries#
PR #29983 (fix: clear loop/iteration metadata when pasting outside container) introduced a pastedToNestedBlock flag: when a node is pasted outside a container, all five metadata fields (parentId, isInLoop, loop_id, isInIteration, iteration_id) are reset to undefined/false. Without this fix, nodes copied from inside a Loop would carry stale loop_id into the top-level graph, confusing the backend execution engine.
Known Issue: parentId and Assign Variable#
Issue #38246 (v1.14.2β1.15.0) documents a regression where Assign Variable nodes created inside a Loop had isInLoop: true and loop_id set correctly, but parentId was missing. The backend loop execution engine uses parentId to build the container's sub-graph; without it, these nodes are silently excluded β they show zero execution records and no "skipped" status. Code nodes in the same Loop were unaffected, confirming the bug is in the Assign Variable node's frontend creation path.
Key Files#
| File | Role |
|---|---|
web/app/components/workflow/hooks/use-available-blocks.ts | availableBlocksFilter β defines which node types are allowed inside containers |
web/app/components/workflow/utils/workflow-init.ts | initialNodes / initialEdges / preprocessNodesAndEdges β stamps container metadata at graph load |
web/app/components/workflow/hooks/use-nodes-interactions.ts | Node creation, drag-drop, paste β where parentId, isInLoop, etc. are written |