microsoft/aspire · error · DistributedApplicationException

Java application ' ' has no in ' ' or in the build root…

Error message

Java application '{resource.Name}' has no {wrapperName} in '{resource.WorkingDirectory}' or in the build root above it. Aspire runs Java applications through the project's own wrapper so that every build uses the tool version the repository pins. Generate one with {GenerateWrapperCommand(tool)}, or point at an existing wrapper with WithWrapperPath.

What it means

Aspire runs Java applications through the project's own wrapper so every build uses the tool version the repository pins. When neither the resource's working directory nor the build root above it contains the default wrapper (mvnw/mvnw.cmd or gradlew/gradlew.bat), and no WithWrapperPath was given, ValidateWrapperExists throws with instructions to generate a wrapper or point at an existing one.

Solutions

  1. Generate the wrapper in the project: 'mvn wrapper:wrapper' for Maven or 'gradle wrapper' for Gradle, and commit it
  2. Point at an existing wrapper elsewhere with WithWrapperPath("path/to/wrapper")
  3. Move/copy the wrapper into the resource's working directory or the build root above it

Example fix

// before
// project has no mvnw; AppHost: builder.AddJavaApp("app", dir).WithMavenGoal("spring-boot:run")
// after: run 'mvn wrapper:wrapper' in the project, then
builder.AddJavaApp("app", dir).WithMavenGoal("spring-boot:run"); // mvnw now found
Defensive patterns

Strategy: validation

Validate before calling

var wrapperName = OperatingSystem.IsWindows() ? "mvnw.cmd" : "mvnw"; // or gradlew(.bat)
if (!File.Exists(Path.Combine(workingDirectory, wrapperName)) && !File.Exists(Path.Combine(buildRoot, wrapperName)))
{
    // run 'mvn wrapper:wrapper' or 'gradle wrapper' before starting the AppHost
}

Type guard

bool HasWrapper(string dir, string name) => File.Exists(Path.Combine(dir, name));

Try / catch

try { ConfigureJavaApp(); }
catch (DistributedApplicationException ex) when (ex.Message.Contains("has no mvnw") || ex.Message.Contains("has no gradlew"))
{
    // generate the wrapper or point at one via WithWrapperPath
}

Prevention

When it happens

Trigger: AddJavaApp with a build-tool launch (WithMavenGoal/WithGradleTask or WithMavenBuild/WithGradleBuild) where the project directory lacks the wrapper script; the wrapper exists only in the developer's global installation, not the repo.

Common situations: Cloning a repo without wrapper files (gitignored or never committed); on Windows expecting 'gradlew' instead of 'gradlew.bat'; project layout where the wrapper lives in a parent directory outside the searched build root.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.Java/JavaHostingExtensions.cs:1545

    {
        var wrapperPath = JavaBuildToolResolver.ResolveWrapperPath(resource, tool, OperatingSystem.IsWindows());

        if (File.Exists(wrapperPath))
        {
            return;
        }

        if (resource.HasAnnotationOfType<WrapperAnnotation>())
        {
            throw new DistributedApplicationException(
                $"Java application '{resource.Name}' has no wrapper at '{wrapperPath}'. That path came " +
                $"from {nameof(WithWrapperPath)} and is resolved relative to the application's working " +
                $"directory '{resource.WorkingDirectory}'.");
        }

        var wrapperName = JavaBuildToolResolver.GetDefaultWrapperName(tool, OperatingSystem.IsWindows());

        throw new DistributedApplicationException(
            $"Java application '{resource.Name}' has no {wrapperName} in '{resource.WorkingDirectory}' " +
            $"or in the build root above it. Aspire runs Java applications through the project's own " +
            $"wrapper so that every build uses the tool version the repository pins. Generate one with " +
            $"{GenerateWrapperCommand(tool)}, or point at an existing wrapper with {nameof(WithWrapperPath)}.");
    }

    /// <summary>
    /// Arranges for the resource's wrapper to be validated once its configuration is final.
    /// </summary>
    private static IResourceBuilder<T> WithDeferredWrapperValidation<T>(
        this IResourceBuilder<T> builder,
        JavaBuildTool tool) where T : JavaAppResource
    {
        // WithMavenGoal and WithMavenBuild both want this, and either may be called more than once, so
        // the subscription is registered at most once per tool.
        if (builder.Resource.Annotations.OfType<JavaWrapperValidationAnnotation>().Any(a => a.Tool == tool))
        {
            return builder;

View on GitHub (pinned to 25830f84bd)