Decorate a function with @task.branch and return the task_id (or list of task_ids) of the branch to execute; returning an empty list skips all branches
Set trigger_rule=TriggerRule.NONE_FAILED_MIN_ONE_SUCCESS on downstream join tasks so they execute regardless of which branch was skipped
Use @task.branch with multiple_outputs=False; the return value must be a string task_id or list of string task_ids, not XCom data
If using traditional operators, use BranchPythonOperator with python_callable returning task_id strings and set depends_on_past=False on downstream tasks to avoid blocking on skipped tasks
Test branching logic independently by mocking the upstream XCom value and asserting the correct branch task_id is returned
Known gotchas
Skipped tasks propagate their skipped state to all direct successors unless trigger_rule is overridden; forgetting to set NONE_FAILED_MIN_ONE_SUCCESS on the join task causes the join to be skipped even when its chosen branch succeeded
A @task.branch function that raises an exception marks all downstream tasks as upstream_failed, not skipped; always wrap conditional logic in try/except and return a default branch on error
Dynamic task mapping is incompatible with @task.branch if the branching logic depends on the mapped index; use a single un-mapped branch task that fans out through expand() in the chosen branch
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp