# 2258 Control Flow 786bf81a

> Control Flow API Reference

- Skill: `tools-only/2258-control-flow-786bf81a` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/2258-control-flow-786bf81a`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/2258-control-flow-786bf81a/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tools-only/2258-control-flow-786bf81a

---

# Control Flow API Reference

Control cell execution flow with conditional stops, gating, and threading.

## Conditional Execution

### mo.stop

Halt cell execution based on a condition.

```python
import marimo as mo

# Stop if condition is true
mo.stop(data is None)

# Stop with message
mo.stop(
    not data_loaded,
    mo.md("**Loading data...** Please wait.")
)

# Stop with UI element
mo.stop(
    form.value is None,
    mo.md("**Submit the form to continue.**")
)
```

When `mo.stop()` triggers:
1. A `MarimoStopError` is raised
2. Cell execution halts
3. The optional output is displayed
4. Downstream cells don't run (their definitions are removed)

### Common Patterns

**Gate on button click:**

```python
run_button = mo.ui.run_button(label="Run Analysis")

# In another cell
mo.stop(not run_button.value, mo.md("Click **Run Analysis** to proceed."))

# Expensive computation runs only after button click
result = expensive_analysis(data)
```

**Gate on form submission:**

```python
form = mo.ui.text(label="Enter query").form()

# In another cell
mo.stop(form.value is None, mo.md("Submit a query to search."))

results = search(form.value)
```

**Gate on data availability:**

```python
mo.stop(
    df is None or len(df) == 0,
    mo.callout("No data available. Upload a file to continue.", kind="warn")
)

# Process data
summary = df.describe()
```

**Multiple conditions:**

```python
errors = []
if not username:
    errors.append("Username required")
if not password:
    errors.append("Password required")

mo.stop(
    len(errors) > 0,
    mo.callout(mo.md("\n".join(f"- {e}" for e in errors)), kind="danger")
)
```

## Periodic Execution

### mo.ui.refresh

Trigger cell re-execution at intervals.

```python
# Auto-refresh every 5 seconds
refresh = mo.ui.refresh(interval=5)

# With options
refresh = mo.ui.refresh(
    options=["1s", "5s", "10s", "30s", "off"],
    default_interval="5s"
)

# In another cell - reference refresh to trigger updates
_ = refresh.value
current_time = datetime.now()
latest_data = fetch_data()
```

## Runtime Configuration

### Lazy Execution Mode

Configure via notebook settings to mark cells as stale instead of auto-running.

In the editor:
1. Open settings (gear icon)
2. Enable "Lazy execution"
3. Cells show "stale" indicator when dependencies change
4. Click to run manually

Useful for:
- Expensive computations
- Debugging
- Step-by-step execution

### Disable Autorun

In notebook settings:
- Disable "Autorun on startup"
- Disable "Autorun on cell execution"

## Threading

### mo.Thread

Extended threading with frontend communication.

```python
import marimo as mo

def worker():
    thread = mo.current_thread()
    for i in range(100):
        if thread.should_exit:
            # Cleanup and exit
            return
        # Do work
        mo.output.append(f"Progress: {i}%")

thread = mo.Thread(target=worker)
thread.start()
```

Properties:
- `should_exit`: Returns `True` when cell becomes invalid (re-run, deleted, interrupted)

Features:
- `mo.output.append()` and `print()` forward to frontend
- Must implement cleanup when `should_exit` is `True`

### mo.current_thread

Get the current marimo thread context.

```python
def worker():
    thread = mo.current_thread()
    while not thread.should_exit:
        # Process items
        pass

# Raises RuntimeError if not in a marimo thread
```

## Error Handling

```python
try:
    result = risky_operation()
except Exception as e:
    mo.stop(True, mo.callout(f"Error: {e}", kind="danger"))

# Continue with result
process(result)
```

## Combining Control Flow

```python
# Complex gating example
file = mo.ui.file(filetypes=[".csv"])
run_btn = mo.ui.run_button(label="Analyze")

mo.vstack([file, run_btn])

# In processing cell
mo.stop(file.value is None, mo.md("Upload a CSV file"))
mo.stop(not run_btn.value, mo.md("Click **Analyze** to process"))

# Both conditions met - process
df = pd.read_csv(io.BytesIO(file.value[0].contents))
analysis = analyze(df)
```

## Best Practices

1. **Use mo.stop() for prerequisites**: Gate expensive operations until dependencies are ready
2. **Provide clear feedback**: Always include informative output in `mo.stop()`
3. **Use run_button for expensive ops**: Don't auto-run costly computations
4. **Handle thread cleanup**: Always check `should_exit` in long-running threads
5. **Prefer reactivity over manual control**: Let marimo's DAG handle most execution flow

