# How to Debug YAML Flows in Maestro: Capturing Debug Bundles and Artifacts

> Debug YAML flows in Maestro effortlessly. Capture UI hierarchies, screenshots, and logs with the --debug-output flag to quickly fix command failures.

- Repository: [Maestro/Maestro](https://github.com/mobile-dev-inc/Maestro)
- Tags: how-to-guide
- Published: 2026-03-20

---

**Enable the `--debug-output` flag when running `maestro test` to automatically capture UI hierarchies, screenshots, and JSON execution logs that pinpoint exactly why any command fails.**

Maestro (mobile-dev-inc/Maestro) is an open-source mobile UI testing framework that executes flows written in YAML. When a flow fails, the framework produces a **debug bundle** containing full execution metadata, allowing you to inspect the exact UI state and command history without guessing what went wrong.

## Enabling Debug Output

The CLI provides two flags to control debug artifact generation. Both are parsed in [[`TestCommand.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/TestCommand.kt)](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/command/TestCommand.kt) (lines 146-160).

| Flag | Purpose | Source Location |
|------|---------|----------------|
| `--debug-output <path>` | Specifies the output directory (defaults to `<home>/maestro/debug`). | [`TestCommand.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/TestCommand.kt) lines 146-151 |
| `--flatten-debug-output` | Flattens the directory structure—writes all files directly to the output folder instead of nested subdirectories. Ideal for CI artifact collection. | [`TestCommand.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/TestCommand.kt) lines 158-160 |

Run your flow with debug capture enabled:

```bash
maestro test my_flow.yaml --debug-output ./debug

```

This creates a directory structure at `./debug/my_flow/` containing [`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json), a `screenshots/` folder, and [`hierarchy.xml`](https://github.com/mobile-dev-inc/Maestro/blob/main/hierarchy.xml) (if a UI inspection failure occurred).

## What Gets Recorded

The debug bundle aggregates four distinct artifact types produced by coordinated components across `maestro-cli` and `maestro-orchestra`.

**Debug JSON ([`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json))**
A complete execution log written after the flow finishes (success or failure). It contains ordered command metadata including status, evaluated expressions, and error messages. Generated by [`TestDebugReporter.saveFlow`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/report/TestDebugReporter.kt#L78-L88).

**Screenshots**
PNG files captured before and after each command depending on its status (pending, completed, failed, or warned). Produced by [`ScreenshotUtils.takeDebugScreenshot`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/util/ScreenshotUtils.kt#L12-L27).

**UI Hierarchy**
An XML (or JSON) dump of the complete view hierarchy captured at the moment a UI-dependent command fails—such as when an element selector returns no matches. Implemented in [[`Orchestra.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Orchestra.kt)](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt#L425-L435).

**Command Metadata**
In-memory state accumulated during execution within the `FlowDebugOutput` data class. This container tracks every command's lifecycle and any exceptions thrown. Defined in [[`TestDebugReporter.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/TestDebugReporter.kt)](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/report/TestDebugReporter.kt#L230-L242):

```kotlin
data class FlowDebugOutput(
    val commands: MutableMap<Command, CommandDebugMetadata> = mutableMapOf(),
    val screenshots: MutableList<ScreenshotMetadata> = mutableListOf(),
    var exception: Exception? = null
)

```

## How the Debug Bundle Is Built

The framework constructs the debug bundle through a five-stage pipeline involving the CLI, test runner, and orchestra modules.

1. **CLI Bootstrapping** — `TestCommand.call()` installs the debug reporter with your specified path.
   ```kotlin
   TestDebugReporter.install(
       debugOutputPathAsString = debugOutput,
       flattenDebugOutput = flattenDebugOutput,
       printToConsole = parent?.verbose == true,
   )
   ```

   *Source:* [[`TestCommand.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/TestCommand.kt)](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/command/TestCommand.kt) lines 46-51.

2. **Flow Initialization** — `TestRunner.runFlow()` instantiates a fresh `FlowDebugOutput` container for each flow execution.
   ```kotlin
   val debugOutput = FlowDebugOutput()
   ```

   *Source:* [[`TestRunner.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/TestRunner.kt)](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/runner/TestRunner.kt) lines 56-58.

3. **Command Execution** — `MaestroCommandRunner` runs each command and immediately records screenshots to the `debugOutput` object, updating status from `PENDING` to final state.
   ```kotlin
   ScreenshotUtils.takeDebugScreenshotByCommand(maestro, debugOutput, CommandStatus.PENDING)
   // ... execution ...
   debugOutput.commands[command] = CommandDebugMetadata(...)
   ```

   *Source:* [`MaestroCommandRunner.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/MaestroCommandRunner.kt) lines 101-113, 125-128.

4. **Failure Handling** — When a command fails, `Orchestra` captures the view hierarchy and enriches the error message with debugging guidance.
   ```kotlin
   val hierarchyRoot = maestro.viewHierarchy().root
   debugMessage = "Element with $description not found. Check the UI hierarchy in debug artifacts ..."
   ```

   *Source:* [[`Orchestra.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Orchestra.kt)](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt) lines 425-436.

5. **Persistence** — After completion, `TestDebugReporter.saveFlow` writes the JSON file and creates the directory structure.
   ```kotlin
   Files.createDirectories(debugOutput)
   TestDebugReporter.saveFlow(flowName, debugOutput, debugOutputPath)
   ```

   *Source:* [[`TestDebugReporter.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/TestDebugReporter.kt)](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/report/TestDebugReporter.kt) lines 183-194.

## Inspecting the Debug Artifacts

Analyze the generated files to diagnose failures without reproducing the issue manually.

**[`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json)**
Open this file in any JSON viewer to see an ordered list of commands with their statuses, evaluated arguments, duration in milliseconds, and error messages. Each command entry includes a `debugMessage` field that often contains the specific selector or expression that failed.

**`screenshots/`**
Browse PNG files named by command index (e.g., `0_failed.png`, `1_completed.png`). Compare the screenshot taken before the command executed with the one taken after to visualize UI transitions and timing issues.

**[`hierarchy.xml`](https://github.com/mobile-dev-inc/Maestro/blob/main/hierarchy.xml)**
Import this file into Android Studio's Layout Inspector or view it in any XML editor to see the complete UI tree at the exact moment of failure. Use this to verify that element selectors in your YAML match actual view attributes.

**Quick Inspection Workflow**

```bash

# Run the flow

maestro test ./flows/checkout.yaml --debug-output ./debug

# Navigate to the generated bundle

cd ./debug/checkout

# View the UI hierarchy (macOS example)

open hierarchy.xml

# Examine the failing command's screenshot

open screenshots/3_failed.png

# Query the JSON log for failed commands

cat debug.json | jq '.commands[] | select(.status == "FAILED")'

```

## Common Debugging Scenarios

**Element Not Found Errors**
When `tapOn` or `assertVisible` fails, open [`hierarchy.xml`](https://github.com/mobile-dev-inc/Maestro/blob/main/hierarchy.xml) to verify the element exists and check the `debugMessage` in [`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json) for the exact selector that failed. The message explicitly references the UI hierarchy artifact for verification.

**Timing and Flakiness**
Check the `durationMs` field in [`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json) for each command. If a command fails after a very short duration (e.g., < 500ms), the UI likely hadn't settled. Add explicit `waitFor` commands or increase retry counts in your YAML.

**Screenshot Assertion Mismatches**
When using `assertScreenshot`, compare the expected baseline with the actual screenshot saved in `screenshots/`. The debug bundle also stores a diff image highlighting pixel differences when the assertion fails.

**Unexpected Crashes**
The `exception` field in [`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json) contains the full stack trace of any unhandled exception, while the `commands` map shows exactly which instruction was executing when the crash occurred.

## Summary

- **Enable debug mode** with `--debug-output <path>` to automatically capture comprehensive failure data.
- **Key artifacts** include [`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json) (execution log), `screenshots/` (visual state), and [`hierarchy.xml`](https://github.com/mobile-dev-inc/Maestro/blob/main/hierarchy.xml) (UI tree).
- **Source coordination** involves `TestCommand`, `TestDebugReporter`, `ScreenshotUtils`, and `Orchestra` working together to build the bundle.
- **Inspect hierarchies** to validate selectors, check screenshots to verify visual state, and query [`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json) for precise error messages and timing data.
- **Use `--flatten-debug-output`** in CI pipelines to simplify artifact archival.

## Frequently Asked Questions

### Where does Maestro store debug output by default?

If you omit the `--debug-output` flag, Maestro writes debug bundles to `<home>/maestro/debug/<flow-name>/`. You can override this path by providing a specific directory to the flag, such as `--debug-output ./ci-artifacts`.

### Can I view the debug bundle on CI without downloading files?

Yes. Use `--flatten-debug-output` to ensure all files sit in a single directory, then configure your CI system (GitHub Actions, GitLab CI, etc.) to upload that folder as a pipeline artifact. Most CI UIs allow browsing PNG screenshots and JSON files directly in the browser.

### Why is [`hierarchy.xml`](https://github.com/mobile-dev-inc/Maestro/blob/main/hierarchy.xml) missing from my debug bundle?

The UI hierarchy is only captured when a command that inspects the UI fails—typically an element lookup like `tapOn` or `assertVisible`. If your flow fails due to a JavaScript error, network timeout, or assertion unrelated to UI elements, the hierarchy file may not be generated because the view state was never queried.

### How do I correlate a screenshot filename with a specific command in my YAML?

Screenshots are named using the command's zero-based index in the flow execution sequence followed by its final status (e.g., `2_failed.png` means the third command failed). Match this index against the `commands` array in [`debug.json`](https://github.com/mobile-dev-inc/Maestro/blob/main/debug.json), which preserves the execution order and includes the command name and arguments for each step.