microsoft/aspire · error · InvalidOperationException
Resource ' ' must have exactly one executable launch…
Error message
Resource '{resource.Name}' must have exactly one executable launch recipe, but {recipes.Length} were found. What it means
Every executable resource must carry exactly one ExecutableLaunchRecipeAnnotation that describes how to launch it. ResolveLaunchPlanAsync throws this InvalidOperationException when the annotation count is not exactly one (zero or multiple), because the launch plan is ambiguous or missing.
Solutions
- Check how the resource is created and ensure exactly one ExecutableLaunchRecipeAnnotation is added
- Search the model annotations for duplicates if your code adds the annotation manually
- Update integration packages to versions compatible with your Aspire.Hosting version
Example fix
// before (annotation added twice)
resource.WithAnnotation(new ExecutableLaunchRecipeAnnotation { ... });
resource.WithAnnotation(new ExecutableLaunchRecipeAnnotation { ... });
// after
resource.WithAnnotation(new ExecutableLaunchRecipeAnnotation { ... }); Defensive patterns
Strategy: validation
Validate before calling
var recipes = resource.Annotations.OfType<ExecutableLaunchRecipeAnnotation>().ToArray();
if (recipes.Length != 1)
{
throw new InvalidOperationException($"{resource.Name} must have exactly one ExecutableLaunchRecipeAnnotation, found {recipes.Length}.");
} Type guard
bool HasSingleLaunchRecipe(IResource r) => r.Annotations.Count(a => a is ExecutableLaunchRecipeAnnotation) == 1;
Try / catch
try
{
await creator.CreateObjectAsync(renderedResource, resourceLogger, ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("exactly one executable launch recipe"))
{
// Inspect resource annotations; add or de-duplicate the launch recipe annotation.
} Prevention
- Always add exactly one ExecutableLaunchRecipeAnnotation in custom resource builders
- Audit builders for accidental duplicate annotation calls
- Keep integration packages in sync with Aspire.Hosting versions
When it happens
Trigger: Resolving the launch plan for a resource whose Annotations contain zero ExecutableLaunchRecipeAnnotation (e.g., the resource type forgot to add it) or more than one (conflicting annotations added twice).
Common situations: Custom resource types built without calling the standard annotation setup; a resource builder accidentally adding the launch recipe annotation twice; version mismatch between an integration package and Aspire.Hosting.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- The executable launch recipe for resource
- Failed to apply launch configuration for resource
- Failed to create executable launch plan for resource
- A JSON Patch operation must contain string 'op' and 'path'…
- All resources should be of the same kind when calling…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/a158f4db8e4fb802.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting/Dcp/ExecutableCreator.cs:129
await factory
.CreateDcpObjectsAsync([renderedResource.DcpResource], cancellationToken)
.ConfigureAwait(false);
}
internal static async Task<ExecutableLaunchPlan> ResolveLaunchPlanAsync(
IResource resource,
IExecutionConfigurationResult executionConfiguration,
IConfiguration configuration,
DistributedApplicationOptions distributedApplicationOptions,
ExecutableLaunchPolicy launchPolicy,
ILogger resourceLogger,
CancellationToken cancellationToken)
{
var recipes = resource.Annotations.OfType<ExecutableLaunchRecipeAnnotation>().ToArray();
if (recipes.Length != 1)
{
throw new InvalidOperationException(
$"Resource '{resource.Name}' must have exactly one executable launch recipe, but {recipes.Length} were found.");
}
var decision = launchPolicy.Decide(resource);
var context = new ExecutableLaunchContext(
resource,
configuration,
distributedApplicationOptions,
executionConfiguration,
decision,
resourceLogger,
cancellationToken);
var plan = await recipes[0].Recipe.CreateLaunchPlanAsync(context).ConfigureAwait(false);
if (plan.Mechanism != decision.Mechanism)
{
throw new InvalidOperationException(
$"The executable launch recipe for resource '{resource.Name}' returned a {plan.Mechanism} plan after {decision.Mechanism} was selected.");View on GitHub (pinned to 25830f84bd)