microsoft/aspire · error · InvalidOperationException
The Java AppHost project in
Error message
The Java AppHost project in '{projectDirectory.FullName}' declares a {GetDisplayName(toolchain)} build but ships no {wrapperName}, and none was found at an enclosing build root. Generate one with '{generateCommand}', or remove the build file to build the AppHost with javac instead. What it means
When a Java AppHost declares a Maven or Gradle build file, the toolchain resolver insists on using that tool's wrapper (mvnw/gw). If no wrapper exists in the project or any enclosing build root, resolution fails rather than silently falling back to a globally installed Maven/Gradle, so builds stay reproducible.
Solutions
- Run 'mvn wrapper:wrapper' (Maven) or 'gradle wrapper' (Gradle) in the AppHost project directory to generate the wrapper
- Commit the generated mvnw/gradlew scripts and .mvn/gradle wrapper properties to the repository
- Remove the pom.xml/build.gradle file if you intentionally want the AppHost built with plain javac instead
Example fix
// before: pom.xml present, no wrapper $ aspire run # -> error: ships no wrapper // after $ cd <AppHostProjectDir> $ mvn wrapper:wrapper $ git add mvnw .mvn/ && git commit
Defensive patterns
Strategy: validation
Validate before calling
var wrapper = toolchain == Maven ? "mvnw" : "gradlew";
if (!File.Exists(Path.Combine(appHostDir, wrapper)) && !FindUpward(appHostDir, wrapper, out _))
throw new InvalidOperationException($"Run '{genCmd}' in {appHostDir} before invoking the CLI"); Type guard
bool HasWrapper(DirectoryInfo dir) => File.Exists(Path.Combine(dir.FullName, "mvnw")) || File.Exists(Path.Combine(dir.FullName, "gradlew"));
Try / catch
try { var invocation = resolver.GetToolInvocation(projectDir, ...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("ships no"))
{
// prompt user to generate the wrapper
} Prevention
- Always generate and commit mvnw/gradlew with the repository
- Don't gitignore wrapper scripts or .mvn/gradle-wrapper.properties
- Check for the wrapper before scripting CI that invokes the AppHost
When it happens
Trigger: Calling GetToolInvocation for a Java AppHost directory containing a pom.xml/build.gradle but no matching wrapper script anywhere up the directory tree.
Common situations: Cloning a repo without wrapper files (gitignored or generated by a tool), scaffolding a Maven/Gradle project by hand and forgetting 'mvn wrapper:wrapper' or 'gradle wrapper', or running the CLI from a subdirectory whose parent lacks the wrapper.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- Directory ' ' contains both Maven and Gradle build files…
- Java application ' ' has no wrapper at ' '. That path came…
- Java application ' ' has no in ' ' or in the build root…
- ArgumentOutOfRangeException: Specified argument was out of…
- ArgumentOutOfRangeException: Specified argument was out of…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/4a99d64f6651a984.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Projects/JavaAppHostToolchainResolver.cs:312
DirectoryInfo appHostDirectory,
JavaAppHostToolchain toolchain)
{
var (wrapperName, generateCommand) = toolchain switch
{
// -N keeps the goal from recursing into the modules of a multi-module build.
JavaAppHostToolchain.Maven => (OperatingSystem.IsWindows() ? "mvnw.cmd" : "mvnw", "mvn -N wrapper:wrapper"),
JavaAppHostToolchain.Gradle => (OperatingSystem.IsWindows() ? "gradlew.bat" : "gradlew", "gradle wrapper"),
_ => throw new ArgumentOutOfRangeException(nameof(toolchain), toolchain, null)
};
var wrapperPath = FindWrapper(projectDirectory, wrapperName, toolchain);
// A globally installed Maven or Gradle is deliberately not used as a fallback: the wrapper pins the
// tool version in the repository, so every machine builds the AppHost with the same one. Falling
// back silently would make the AppHost build depend on whatever the developer happens to have.
if (wrapperPath is null)
{
throw new InvalidOperationException(
$"The Java AppHost project in '{projectDirectory.FullName}' declares a {GetDisplayName(toolchain)} " +
$"build but ships no {wrapperName}, and none was found at an enclosing build root. Generate one " +
$"with '{generateCommand}', or remove the build file to build the AppHost with javac instead.");
}
var wrapperDirectory = new DirectoryInfo(Path.GetDirectoryName(wrapperPath)!);
if (!OperatingSystem.IsWindows())
{
// Invoked through "sh" rather than executed directly because a wrapper checked out on
// Windows, or committed without its mode bit, arrives without the executable bit and
// exec fails with "Permission denied". The wrappers are POSIX shell scripts and are
// documented to be run that way, so "sh <path>" is always valid. This matches how the
// hosted Java resources invoke wrappers (JavaHostingExtensions.WrapperInvocationFor).
//
// The absolute path is kept because the process is started without a shell, so a bare
// "mvnw" would be looked up on PATH and never found in the project directory.
return new JavaToolInvocation("sh", [wrapperPath], wrapperDirectory);View on GitHub (pinned to 25830f84bd)