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

  1. Check how the resource is created and ensure exactly one ExecutableLaunchRecipeAnnotation is added
  2. Search the model annotations for duplicates if your code adds the annotation manually
  3. 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

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


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)