microsoft/aspire · error · DistributedApplicationException

Java application ' ' has no wrapper at ' '. That path came…

Error message

Java application '{resource.Name}' has no wrapper at '{wrapperPath}'. That path came from WithWrapperPath and is resolved relative to the application's working directory '{resource.WorkingDirectory}'.

What it means

WithWrapperPath let the user point at a custom build-tool wrapper, but the wrapper file does not exist at the given location. ValidateWrapperExists throws a DistributedApplicationException stating that the path came from WithWrapperPath and is resolved relative to the application's working directory.

Solutions

  1. Fix the path passed to WithWrapperPath so it points at the existing wrapper relative to the resource's working directory
  2. Move or restore the wrapper script (mvnw/mvnw.cmd or gradlew/gradlew.bat) into the expected location
  3. Remove WithWrapperPath to fall back to the default wrapper resolution from the working directory or build root

Example fix

// before
.AddJavaApp("app", dir).WithWrapperPath("tools/mvnw") // file not there
// after
.AddJavaApp("app", dir).WithWrapperPath("mvnw") // wrapper sits directly in the working directory
Defensive patterns

Strategy: validation

Validate before calling

var wrapperPath = Path.GetFullPath(Path.Combine(workingDirectory, relativeWrapperPath));
if (!File.Exists(wrapperPath))
{
    throw new FileNotFoundException($"Wrapper not found at {wrapperPath}; fix the WithWrapperPath argument.");
}

Type guard

bool WrapperExists(string workingDir, string relPath) => File.Exists(Path.Combine(workingDir, relPath));

Try / catch

try { ConfigureJavaApp(); }
catch (DistributedApplicationException ex) when (ex.Message.Contains("has no wrapper at"))
{
    // fix the WithWrapperPath value or restore the wrapper file
}

Prevention

When it happens

Trigger: Calling WithWrapperPath("some/path/mvnw") where no file exists at workingDirectory/some/path/mvnw; validation runs via WithDetectedBuildTool or deferred wrapper validation.

Common situations: Typo or wrong casing in the wrapper path; passing an absolute path when resolution is relative to WorkingDirectory; the wrapper file was gitignored or not copied into the build context; Windows/macOS path-separator mistakes.

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

Appendix: source

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

    /// Throws when the wrapper the resource will launch is not on disk.
    /// </summary>
    /// <remarks>
    /// Deferred to resource start so that the whole AppHost has been authored first: only then is it
    /// known whether a <see cref="WithWrapperPath{T}(IResourceBuilder{T}, string)"/> override supplied
    /// the wrapper that the default location lacks.
    /// </remarks>
    private static void ValidateWrapperExists(JavaAppResource resource, JavaBuildTool tool)
    {
        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>(

View on GitHub (pinned to 25830f84bd)