microsoft/aspire · error · ArgumentException

The launch configuration producer returns

Error message

The launch configuration producer returns '{typeof(TLaunchConfiguration)}'. An asynchronous producer must bind to an asynchronous {nameof(WithDebugSupport)} overload either by accepting the launch mode and a {nameof(CancellationToken)} or by accepting a {nameof(LaunchConfigurationCallbackContext)}; otherwise the task itself is used as the launch configuration.

What it means

This ArgumentException is thrown when a synchronous WithDebugSupport overload is given a producer delegate that returns Task/ValueTask. A sync overload would treat the Task object itself as the launch configuration instead of awaiting it, so the library detects this at registration time and fails fast. The producer must instead bind to the async overload that accepts the launch mode plus a CancellationToken, or one accepting LaunchConfigurationCallbackContext.

Solutions

  1. Change the producer to the async overload of WithDebugSupport that accepts (launchMode, CancellationToken) or (LaunchConfigurationCallbackContext)
  2. Remove async/await and return the launch configuration synchronously if no async work is needed
  3. Check the generic type arguments: the producer's return type must not be Task or ValueTask for the sync overload

Example fix

// before
builder.WithDebugSupport(async () => await ProduceLaunchConfigAsync());
// after
builder.WithDebugSupport(async (launchMode, cancellationToken) =>
    await ProduceLaunchConfigAsync(launchMode, cancellationToken));
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof(Task).IsAssignableFrom(producerType) || producerType == typeof(ValueTask<LaunchConfiguration>) || producerType == typeof(ValueTask))
    throw new InvalidOperationException("Use the async WithDebugSupport overload for Task/ValueTask-returning producers.");

Type guard

bool IsAsyncProducer<TLaunchConfiguration>() => typeof(Task).IsAssignableFrom(typeof(TLaunchConfiguration)) || typeof(TLaunchConfiguration) == typeof(ValueTask);

Try / catch

try { builder.WithDebugSupport(producer); }
catch (ArgumentException ex) when (ex.ParamName == nameof(producer)) { logger.LogError(ex, "Producer returns Task; use the async overload"); }

Prevention

When it happens

Trigger: Calling builder.WithDebugSupport<TResource, Task<TLaunchConfiguration>>(...) or a ValueTask-returning producer against the synchronous overload of WithDebugSupport, typically by writing an async lambda without choosing the async overload.

Common situations: Using C# async lambda syntax that infers Task<T> as the return type while calling the sync overload, or after a refactor changed an overload's signature; common in debug-support/launch-configuration code.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting/ResourceBuilderExtensions.cs:4909

    /// later only for executable creations where this debug-support annotation is active, including restarts and replicas.
    /// The callback does not run for unsupported debug sessions, publish mode, or inactive annotations superseded by
    /// a later <see cref="SupportsDebuggingAnnotation"/>.
    /// </remarks>
    [OverloadResolutionPriority(-1)]
    [Experimental("ASPIREEXTENSION001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")]
    [AspireExportIgnore(Reason = "Generic debug launch configuration support is not part of the ATS surface.")]
    public static IResourceBuilder<T> WithDebugSupport<T, TLaunchConfiguration>(
        this IResourceBuilder<T> builder,
        Func<string, TLaunchConfiguration> launchConfigurationProducer,
        string launchConfigurationType)
        where T : IResource
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(launchConfigurationProducer);

        if (typeof(Task).IsAssignableFrom(typeof(TLaunchConfiguration)) || IsValueTask(typeof(TLaunchConfiguration)))
        {
            throw new ArgumentException(
                $"The launch configuration producer returns '{typeof(TLaunchConfiguration)}'. An asynchronous producer must bind to an asynchronous {nameof(WithDebugSupport)} overload " +
                $"either by accepting the launch mode and a {nameof(CancellationToken)} or by accepting a {nameof(LaunchConfigurationCallbackContext)}; otherwise the task itself is used as the launch configuration.",
                nameof(launchConfigurationProducer));
        }

        return builder.WithDebugSupport(
            (mode, _) => Task.FromResult(launchConfigurationProducer(mode)),
            launchConfigurationType);

        static bool IsValueTask(Type type)
            => type == typeof(ValueTask) || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ValueTask<>));
    }

    /// <summary>
    /// Adds support for asynchronously producing an IDE launch configuration for the resource.
    /// </summary>
    /// <typeparam name="T">The resource type.</typeparam>
    /// <typeparam name="TLaunchConfiguration">The launch configuration type produced for the resource, typically derived from <see cref="ExecutableLaunchConfiguration"/>.</typeparam>

View on GitHub (pinned to 25830f84bd)