Back to examples
Error Handling

Fail Fast

Cancel pending tasks as soon as one task fails.

When to use

Use `fail_fast=True` when a failing task means the rest of the pipeline is pointless. This saves compute and surfaces errors faster.

error-handling/fail-fast.py
"""
06_fail_fast.py: Early pipeline termination with fail_fast=True.

Demonstrates:
- run_local(fail_fast=True): as soon as one task fails, pending tasks are cancelled
- A flow with two independent tasks where one always raises
"""

from dagy import flow, task


@task
def always_succeeds(label: str) -> str:
    print(f"  {label}: completed successfully")
    return f"ok:{label}"


@task
def always_fails(label: str) -> str:
    print(f"  {label}: about to raise!")
    raise RuntimeError(f"Task '{label}' encountered a fatal error")


@flow(name="fail_fast_flow")
def fail_fast_flow() -> None:
    always_succeeds("task-A")
    always_fails("task-B")   # This will fail and trigger fail_fast
    always_succeeds("task-C")  # Should be cancelled before it runs


if __name__ == "__main__":
    from dagy.local.errors import TaskFailedError

    try:
        result = fail_fast_flow.run_local(fail_fast=True)
        print(f"Run ID : {result.run_id}")
        print(f"Status : {result.status}")
    except TaskFailedError as exc:
        # fail_fast=True causes run_local to raise as soon as any task fails.
        print(f"Pipeline stopped early (fail_fast=True): {exc}")

How it works

  1. `always_succeeds` completes normally.
  2. `always_fails` raises a `RuntimeError` immediately.
  3. `always_succeeds` (task-C) is scheduled after task-B but would be cancelled by fail-fast.
  4. `run_local(fail_fast=True)` makes the executor stop and raise as soon as any task fails.
  5. The caller catches `TaskFailedError` to handle early termination gracefully.

Inputs

  • None.

Outputs

  • A `TaskFailedError` exception (pipeline stops early).

Dependencies

  • `dagy`, `dagy.local.errors.TaskFailedError`