All nodes/Logic & Flow/Branching

If / Else

Conditional branching: evaluates an expression and routes execution to the 'true' or 'false' branch. The expression supports comparisons and access to node data: {{nodes.llm_1.output.score}} > 0.8.

If / Else▶
InputTrue
False

Type in the graph: if

Exec

An error output can be enabled to handle failures on their own path.

Ports can be split into separate execution and data handles.

Try it

Minimal working scenario

Entry
If / Else▶
Exit▶
  • Execute + Data
Press “Copy nodes”, open the editor and hit Ctrl+V on the canvas.

Runs as pasted

When to use it

Reach for this node when execution forks on data the graph already has: a score from a model, a field from a document, the length of a text, a flag from the database. There are exactly two branches. When you need more than two directions, use switch — a chain of five conditions reads worse than one list. When a step has to repeat while a condition holds, that is while_loop: a link from a branch back up the graph cannot be built, the scheduler treats such a graph as cyclic and refuses to run it.

How it works

The condition is a template, and it has two working shapes. A whole expression inside braces ({{ nodes.classify.output.score > 0.8 }}) is evaluated and returns a real boolean. The mixed shape ({{ nodes.classify.output.score }} > 0.8) substitutes the value first and then parses the resulting string as one comparison of two literals: >, >=, <, <=, ==, != and nothing else.

The node computes nothing on top of its input: the selected output carries exactly the value that arrived. Downstream templates can read {{ nodes.<id>.output }} — that value — and {{ nodes.<id>.branch }} — the name of the branch that fired, true or false.

The branch not taken is not a failure. Nodes on it end up skipped with the reason “An upstream branch went the other way”, and the run still finishes as completed: run status reports errors, not how many nodes ran.

Common mistakes

  • A compound condition in the mixed shape is always true. After substitution {{ a }} > 1 and {{ b }} < 2 becomes a string that is not a single comparison — and a non-empty string is truthy. Keep the logic inside the braces: {{ a > 1 and b < 2 }}.
  • Text comparison belongs inside the braces too. {{ inputs.input }} == 'yes' stops parsing as an expression after substitution and takes the true branch for any input. Write {{ inputs.input == 'yes' }} instead.
  • A typo in a path silently yields false. A missing path substitutes as empty, and a comparison with an empty operand is not performed. Check what the condition became: the run details show a Rendered templates block for the node, with the unresolved paths listed under the __undefined_refs__ key.
  • An empty condition will not deploy — the expression is required.
  • Both branches into one node is a normal shape. The engine runs that node once, with the payload of the branch that fired; the validator warns about it so you do not expect the values of both branches at the same time.

Inputs

PortWirePayloadNotes
InputinputExecute + Dataexecute_data—

Outputs

PortWirePayloadNotes
TruetrueExecute + Dataexecute_data—
FalsefalseExecute + Dataexecute_data—

Configuration

FieldTypeDefaultDescription
Conditioncondition_expressionstring""

Expression that evaluates to truthy/falsy. Supports comparisons: {{nodes.llm_1.output.confidence}} > 0.8

supports templates

Shared fields

Every node has these three — the platform adds them, not the node author.

  • expose_error_output — When enabled, show an execution output to connect nodes that run if this step fails.
  • split_ports_in — Show separate execution and data input handles instead of one combined port.
  • split_ports_out — Show separate execution and data output handles instead of one combined port.

Ready-made examples using this node