apache/maven · warning

Illegal call to phase '{}'. The main phase '{}' will be used

Error message

Illegal call to phase '{}'. The main phase '{}' will be used instead.

What it means

Maven 4 decorates every lifecycle phase with implicit 'before:<phase>' and 'after:<phase>' sub-phases (Lifecycle.BEFORE/Lifecycle.AFTER prefixes). calculateTaskSegments detects a task string starting with those prefixes, strips it down to the main phase via PhaseId.of(task).phase(), warns that the call was illegal, and schedules the main phase - so 'mvn before:compile' builds up to and including compile, not just the pre-step.

Source

Thrown at impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/DefaultLifecycleTaskSegmentCalculator.java:106

        }

        return calculateTaskSegments(session, tasks);
    }

    @Override
    public List<TaskSegment> calculateTaskSegments(MavenSession session, List<String> tasks)
            throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
                    MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException,
                    PluginVersionResolutionException {
        List<TaskSegment> taskSegments = new ArrayList<>(tasks.size());

        TaskSegment currentSegment = null;

        for (String task : tasks) {
            if (isBeforeOrAfterPhase(task)) {
                String prevTask = task;
                task = PhaseId.of(task).phase();
                LOGGER.warn("Illegal call to phase '{}'. The main phase '{}' will be used instead.", prevTask, task);
            }
            if (isGoalSpecification(task)) {
                // "pluginPrefix[:version]:goal" or "groupId:artifactId[:version]:goal"

                lifecyclePluginResolver.resolveMissingPluginVersions(session.getTopLevelProject(), session);

                MojoDescriptor mojoDescriptor =
                        mojoDescriptorCreator.getMojoDescriptor(task, session, session.getTopLevelProject());

                boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();

                if (currentSegment == null || currentSegment.isAggregating() != aggregating) {
                    currentSegment = new TaskSegment(aggregating);
                    taskSegments.add(currentSegment);
                }

                currentSegment.getTasks().add(new GoalTask(task));
            } else {

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Invoke the plain phase instead: mvn compile rather than mvn before:compile / mvn after:compile
  2. If you only want pre/post hooks, bind your plugin executions to the standard phases in the POM
  3. For programmatic invocations, sanitize the task list by stripping before:/after: prefixes before calling Maven

Example fix

# before
mvn after:package     # warns; runs 'package' itself
mvn before:compile    # warns; runs 'compile' itself
# after
mvn package
mvn compile
Defensive patterns

Strategy: validation

Validate before calling

// programmatic invocation: strip illegal before:/after: prefixes before calling Maven
List<String> sanitized = tasks.stream()
        .map(t -> t.startsWith("before:") || t.startsWith("after:") ? t.substring(t.indexOf(':') + 1) : t)
        .distinct()
        .collect(Collectors.toList());

Prevention

When it happens

Trigger: Passing before:<phase> or after:<phase> as a goal on the command line or via MavenSession goals programmatically; the before/after sub-phases are internal stepping stones, not directly invocable tasks.

Common situations: Users experimenting with Maven 4 phase syntax; scripts generated from phase listings that include the synthetic before:/after: entries; IDE task templates listing all phases.

Related errors


AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21). Data as JSON: /api/errors/e116343c5cc5cddd. Report an issue: GitHub.