# How to Debug Maven Execution Flow: A Complete Guide to Maven's Internal Mechanics

> Master Maven execution flow with this guide. Learn to debug using verbose logging, dry-run mode, EventSpy, and JVM breakpoints in core classes like DefaultLifecycleExecutor.

- Repository: [The Apache Software Foundation/maven](https://github.com/apache/maven)
- Tags: how-to-guide
- Published: 2026-07-06

---

**You can debug Maven execution flow by combining verbose logging (`-X`), dry-run mode (`-DdryRun`), custom EventSpy implementations, and remote JVM debugging breakpoints set in core classes like `DefaultLifecycleExecutor` and `MojoExecutor`.**

Apache Maven orchestrates a sophisticated pipeline that transforms command-line arguments into an ordered sequence of plugin executions. Understanding how to debug Maven execution flow requires visibility into the core classes that manage this transformation, from CLI parsing in `MavenCli` to goal execution in `MojoExecutor`. This guide examines the actual source code from the `apache/maven` repository to show you exactly where and how to intercept the build process for troubleshooting.

## Understanding Maven's Execution Architecture

Maven's build lifecycle follows a precise chain of responsibility, with each component handing off to the next until the final plugin goal executes. The execution flow traverses seven distinct stages, from initial CLI parsing through project resolution to the final mojo invocation. Each stage offers specific hooks for debugging, allowing you to pinpoint exactly where a build hangs, why a plugin is skipped, or how the execution plan is constructed.

## The 7 Stages of Maven Execution Flow

### 1. CLI Parsing and Request Building

The entry point class `org.apache.maven.cli.MavenCli` (located in [`compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java`](https://github.com/apache/maven/blob/main/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java)) handles the initial transformation of command-line arguments into a `MavenExecutionRequest`. This class processes the `-X` (debug) and `-e` (full stacktrace) flags, populating the request object with logging preferences and build options before the core execution begins.

### 2. Session Construction

The `org.apache.maven.DefaultMaven` class (in [`impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java`](https://github.com/apache/maven/blob/main/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java)) constructs the `MavenSession` that serves as the execution context for the entire build. It delegates to `MavenExecutionRequestPopulator` and `MavenExecutionRequestBuilder` to initialize the session with the repository system, project builder, and class realm.

### 3. Effective POM Resolution

The `org.apache.maven.model.building.DefaultModelBuilder` (in [`compat/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java`](https://github.com/apache/maven/blob/main/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java)) reads the root [`pom.xml`](https://github.com/apache/maven/blob/main/pom.xml), interpolates properties, merges parent POMs, and validates the model structure. This stage produces an `EffectiveModel` that ultimately becomes a `MavenProject` used throughout the lifecycle.

### 4. Project Graph Building

The `org.apache.maven.project.DefaultProjectBuilder` (in [`impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java`](https://github.com/apache/maven/blob/main/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java)) detects multi-module projects and their inter-dependencies, calculating the precise order in which modules must be built. It returns a list of `MavenProject` objects representing the reactor, handling circular dependency detection and module ordering.

### 5. Execution Plan Creation

The `org.apache.maven.lifecycle.DefaultLifecycleExecutor` (in [`impl/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java`](https://github.com/apache/maven/blob/main/impl/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java)) determines which plugins must execute for the requested lifecycle phases. It collaborates with `org.apache.maven.lifecycle.MavenExecutionPlan` (in [`impl/maven-core/src/main/java/org/apache/maven/lifecycle/MavenExecutionPlan.java`](https://github.com/apache/maven/blob/main/impl/maven-core/src/main/java/org/apache/maven/lifecycle/MavenExecutionPlan.java)) to walk the lifecycle definition, resolve plugin descriptors, and generate an ordered list of `ExecutionPlanItem`s.

### 6. Mojo Execution

The internal `MojoExecutor` class (in [`impl/maven-core/src/main/java/org/apache/maven/execution/MojoExecutor.java`](https://github.com/apache/maven/blob/main/impl/maven-core/src/main/java/org/apache/maven/execution/MojoExecutor.java)) iterates through the execution plan and invokes `Mojo.execute()` for each goal. It handles forked lifecycles, evaluates skip conditions, and enforces `@requiresProject` checks before delegating to the plugin code.

### 7. Event Broadcasting

Throughout execution, Maven fires `org.apache.maven.eventspy.ExecutionEvent` objects (defined in [`api/maven-event-api/src/main/java/org/apache/maven/eventspy/ExecutionEvent.java`](https://github.com/apache/maven/blob/main/api/maven-event-api/src/main/java/org/apache/maven/eventspy/ExecutionEvent.java)) to signal lifecycle progress. Custom `EventSpy` implementations can intercept these events to monitor start, success, and failure states for each mojo execution.

## Techniques to Debug Maven Execution Flow

### Verbose Logging and Dry Run Mode

The `-X` flag enables verbose debug logging throughout the entire codebase, dumping internal state including effective POMs, repository sessions, and full stack traces. The `-DdryRun` flag constructs the full `MavenExecutionPlan` in `DefaultLifecycleExecutor` but stops before `MojoExecutor` runs any goals, revealing the exact plugin order without side effects.

```bash
mvn -X -DdryRun clean install

```

### Remote JVM Debugging

Set the `MAVEN_OPTS` environment variable to enable the Java Debug Wire Protocol before launching Maven. With this configuration, Maven suspends at startup and waits for a debugger connection on port 8000, allowing you to set breakpoints in `DefaultLifecycleExecutor.calculateExecutionPlan()` or `MojoExecutor.execute()` to step through the actual execution logic.

```bash
export MAVEN_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8000"
mvn clean install

```

### Custom EventSpy Implementation

Create a class extending `org.apache.maven.eventspy.AbstractEventSpy` and package it as a JAR with a [`META-INF/maven/event-spy.xml`](https://github.com/apache/maven/blob/main/META-INF/maven/event-spy.xml) descriptor file. Deploy this JAR to your build using the `-Dmaven.ext.class.path` argument to intercept `ExecutionEvent` objects at the API level, allowing you to log timing data or inspect the `MavenSession` without modifying project POMs.

```java
import org.apache.maven.eventspy.AbstractEventSpy;
import org.apache.maven.execution.ExecutionEvent;

public class ExecutionPlanPrinter extends AbstractEventSpy {
    @Override
    public void onEvent(Object event) {
        if (event instanceof ExecutionEvent) {
            ExecutionEvent ev = (ExecutionEvent) event;
            if (ev.getType() == ExecutionEvent.Type.MojoStarted) {
                System.out.println("Executing: " + 
                    ev.getMojoExecution().getArtifactId() + ":" +
                    ev.getMojoExecution().getGoal());
            }
        }
    }
}

```

Activate the spy with:

```bash
mvn -Dmaven.ext.class.path=/path/to/your-spy.jar clean install

```

### MavenSession Inspection

Access the `MavenSession` object through an `EventSpy` to inspect project state via `getProjects()`, `getCurrentProject()`, and `getPluginContext()` methods. Enable reactor debugging with `-Dmaven.reactor.debug=true` to expose module ordering and up-to-date checks, revealing why Maven skips certain modules or reorders the build sequence.

## Summary

- **Maven execution flow** traverses seven stages from `MavenCli` argument parsing through `MojoExecutor` goal invocation.
- **Verbose mode** (`-X`) and **dry run** (`-DdryRun`) provide immediate visibility into execution plans without custom code.
- **Remote debugging** via `MAVEN_OPTS` allows breakpoint inspection of `DefaultLifecycleExecutor` and `MojoExecutor`.
- **Custom EventSpy** implementations hook into `ExecutionEvent` broadcasts to monitor mojo start and completion events.
- **MavenSession inspection** through `EventSpy` reveals project graphs, plugin contexts, and reactor ordering.

## Frequently Asked Questions

### How do I see exactly which plugins are executing in my Maven build?

Use the `-DdryRun` flag to print the `MavenExecutionPlan` without running goals, or implement a custom `EventSpy` that logs `ExecutionEvent.Type.MojoStarted` events. The dry run outputs the ordered list of `ExecutionPlanItem`s from `DefaultLifecycleExecutor`, while the EventSpy provides real-time confirmation as `MojoExecutor` invokes each goal. Both methods require no modifications to your project POM.

### What is the difference between `-X` debug mode and `-DdryRun`?

The `-X` flag enables verbose logging throughout the entire codebase, showing internal state dumps, dependency resolution, and plugin configuration, but actually executes the build. The `-DdryRun` flag constructs the full execution plan in `MavenExecutionPlan` but stops before `MojoExecutor` runs any goals, making it safe for inspecting build logic without side effects.

### How can I debug why Maven hangs during a specific lifecycle phase?

Attach a remote JVM debugger using `MAVEN_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8000"` and set breakpoints in `DefaultLifecycleExecutor.execute()` or `MojoExecutor.execute()`. When the build suspends, inspect the current `ExecutionPlanItem` to identify which mojo is active. Alternatively, use a custom `EventSpy` to log timestamps between `MojoStarted` and `MojoSucceeded` events to identify long-running goals.

### Can I debug Maven execution without modifying the project POM or source code?

Yes, external debugging requires no POM changes. Use `-X` for immediate logging, `-DdryRun` to validate execution plans, or package a custom `EventSpy` in a separate JAR and activate it via `-Dmaven.ext.class.path`. Remote debugging through `MAVEN_OPTS` also requires zero project modifications, allowing you to step through `apache/maven` core classes like `DefaultMaven` and `DefaultProjectBuilder` without touching the project being built.