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}', or call WithMavenBuild or WithGradleBuild to state how the deployable JAR is produced.

What it means

In the no-detected-build-tool-annotation path, publish tries configured tool, then filesystem detection; if neither yields a tool it throws with an expanded message that also suggests WithMavenBuild/WithGradleBuild. Like error 995, publish cannot proceed without knowing how to produce the deployable JAR.

Solutions

  1. Call WithMavenBuild or WithGradleBuild on the resource to declare the build tool explicitly.
  2. Add a pom.xml or Gradle build/settings file to the directory given to AddJavaApp.
  3. Point AddJavaApp at the directory that actually contains the build file.

Example fix

// before
builder.AddJavaApp("api", "src/api"); // no pom.xml or gradle files
// after
builder.AddJavaApp("api", "src/api")
    .WithGradleBuild(); // or add build.gradle.kts to src/api
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))))
    resourceBuilder.WithGradleBuild(); // declare the tool explicitly

Try / catch

try { /* publish */ }
catch (DistributedApplicationException ex) when (ex.Message.Contains("WithMavenBuild or WithGradleBuild"))
{ /* declare the build tool explicitly and republish */ }

Prevention

When it happens

Trigger: Publishing an AddJavaApp resource with no JavaDetectedBuildToolAnnotation source path leading to a tool: no build-tool annotation from run mode and DetectBuildToolForPublish finds no pom.xml/gradle files in appDirectory.

Common situations: Same as 995: wrong directory, build files elsewhere, exotic build setups; also resources created via paths that never ran run-mode detection.

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/eaed38656d57dc41. Report an issue: GitHub.

Appendix: source

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

            }

            // 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)
            {
                return (detectedTool, DefaultPackageArgs(detectedTool));
            }

            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}', " +
                "or call WithMavenBuild or WithGradleBuild " +
                "to state how the deployable JAR is produced.");
        }

        private static string[] DefaultPackageArgs(JavaBuildTool tool) => tool switch
        {
            // -B disables the ANSI progress output that renders as noise in a build log, and -ntp drops
            // the per-artifact download lines. Tests are skipped because the container build produces a
            // deployable artifact; running the test suite belongs to CI, not to `aspire publish`.
            JavaBuildTool.Maven => ["-B", "-ntp", "-DskipTests", "package"],
            JavaBuildTool.Gradle => ["-x", "test", "build"],
            _ => throw new UnreachableException()
        };

        /// <summary>
        /// Emits a shell command that resolves <paramref name="outputGlob"/> to exactly one JAR and copies

View on GitHub (pinned to 25830f84bd)