microsoft/aspire · error · DistributedApplicationException

The Java application

Error message

The Java application '{resource.Name}' cannot be published because no build tool was found. Add a pom.xml, build.gradle, build.gradle.kts, settings.gradle, or settings.gradle.kts to '{appDirectory}'.

What it means

During publish, Aspire must know whether Maven or Gradle builds the deployable JAR, but no build tool was detected: the JavaBuildToolAnnotation was absent and no pom.xml/gradle build or settings file exists in the app directory. The publish pipeline cannot generate a Dockerfile without knowing the build tool, so it fails with instructions on which marker files it looks for.

Solutions

  1. Add a pom.xml or Gradle build/settings file to the app directory (or point AddJavaApp at the directory that has one).
  2. Call WithMavenBuild or WithGradleBuild to explicitly state the build tool.
  3. Verify appDirectory points at the project root containing the build file.

Example fix

// before
builder.AddJavaApp("api", "repo"); // build files are in repo/server
// after
builder.AddJavaApp("api", "repo/server")
    .WithMavenBuild();
Defensive patterns

Strategy: validation

Validate before calling

var dir = "src/Api";
string[] markers = ["pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"];
if (!markers.Any(m => File.Exists(Path.Combine(dir, m))))
    throw new Exception($"No Maven/Gradle build file found in {dir}");

Try / catch

try { /* publish */ }
catch (DistributedApplicationException ex) when (ex.Message.Contains("no build tool was found"))
{ /* add build file or call WithMavenBuild/WithGradleBuild */ }

Prevention

When it happens

Trigger: Publishing an AddJavaApp resource where the app directory contains none of pom.xml, build.gradle, build.gradle.kts, settings.gradle, settings.gradle.kts, and no explicit WithMavenBuild/WithGradleBuild (JavaBuildToolAnnotation) was applied.

Common situations: Pointing AddJavaApp at the wrong directory (parent or sibling of the actual project); non-standard multi-module layout where build files live in a subdirectory; apps built with a different tool (Ant, Bazel); directory containing only source code.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/2f796222757b10ad. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Java/JavaDockerfileGenerator.cs:1154

            return ["--no-daemon", .. args];
        }

        private static (JavaBuildTool Tool, string[] Args) ResolveConfiguredToolAndArgs(JavaAppResource resource, string appDirectory)
        {
            // A build step configured with WithMavenBuild/WithGradleBuild states both the tool and the
            // arguments that produce a deployable artifact, so it is the most precise source.
            if (resource.TryGetLastAnnotation<JavaBuildStepAnnotation>(out var buildStep))
            {
                return (buildStep.Tool, buildStep.Args);
            }

            if (resource.TryGetLastAnnotation<JavaDetectedBuildToolAnnotation>(out var detected))
            {
                var tool = resource.TryGetLastAnnotation<JavaBuildToolAnnotation>(out var launch)
                    ? launch.Tool
                    : DetectBuildToolForPublish(resource, appDirectory)
                        ?? throw new DistributedApplicationException(
                            $"The Java application '{resource.Name}' cannot be published because no build tool was found. " +
                            $"Add a pom.xml, build.gradle, build.gradle.kts, settings.gradle, or settings.gradle.kts to '{appDirectory}'.");

                return (tool, detected.GetConfiguration(tool).BuildArgs);
            }

            // A launch goal such as spring-boot:run or bootRun identifies the tool but never packages, so
            // only the tool is taken from it and the packaging arguments are defaulted.
            if (resource.TryGetLastAnnotation<JavaBuildToolAnnotation>(out var buildTool))
            {
                return (buildTool.Tool, DefaultPackageArgs(buildTool.Tool));
            }

            // Left for an application added with a prebuilt JAR path and no build configuration: the
            // container still has to produce that JAR, so the tool comes from what is on disk. This uses
            // the same detector as run mode so publish cannot silently choose Maven for an ambiguous
            // directory that run mode rejects.
            if (DetectBuildToolForPublish(resource, appDirectory) is { } detectedTool)

View on GitHub (pinned to 25830f84bd)