microsoft/aspire · critical · InvalidOperationException

Resource ' ' is missing MauiBuildInfoAnnotation. Cannot…

Error message

Resource '{resource.Name}' is missing MauiBuildInfoAnnotation. Cannot proceed with build — the semaphore would be held indefinitely.

What it means

Before a MAUI resource is started, MauiBuildQueueEventSubscriber serializes builds by acquiring a semaphore. The release of that semaphore depends on build-info data, so if the resource lacks a MauiBuildInfoAnnotation the subscriber throws InvalidOperationException rather than deadlocking on the semaphore. Startup of that resource cannot proceed.

Solutions

  1. Create the MAUI platform/project resource through the intended AddMaui*/With* APIs so MauiBuildInfoAnnotation is attached.
  2. Manually add the annotation before start: resource.Annotations.Add(new MauiBuildInfoAnnotation(...)) if constructing resources by hand.
  3. Confirm the resource reaching the event is a MAUI resource — filter in your subscriber with TryGetLastAnnotation<MauiBuildInfoAnnotation> before invoking build logic.

Example fix

// before
builder.AddResource(myMauiApp); // no MAUI annotations
// after
builder.AddMauiProject("my-maui-app", "MyMauiApp.csproj"); // adds MauiBuildInfoAnnotation
Defensive patterns

Strategy: validation

Validate before calling

if (!resource.TryGetLastAnnotation<MauiBuildInfoAnnotation>(out _)) throw new InvalidOperationException($"{resource.Name} is not a MAUI resource; use AddMauiProject/AddMaui* APIs.");

Type guard

static bool IsMauiBuildable(IResource r) => r.TryGetLastAnnotation<MauiBuildInfoAnnotation>(out _);

Try / catch

try { await subscriber.OnBeforeResourceStartedAsync(evt, ct); } catch (InvalidOperationException ex) when (ex.Message.Contains("MauiBuildInfoAnnotation")) { /* resource was not built via MAUI APIs */ }

Prevention

When it happens

Trigger: A resource passed through OnBeforeResourceStartedEvent into RunBuildAsync that was not created/configured via the MAUI hosting APIs that add MauiBuildInfoAnnotation (src/Aspire.Hosting.Maui/Lifecycle/MauiBuildQueueEventSubscriber.cs:175).

Common situations: Adding a plain ProjectResource or custom resource and expecting MAUI build queue behavior; calling a MAUI extension on the wrong resource type; a custom pipeline stripping annotations; version drift where the annotation is added by a newer extension method.

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


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

Appendix: source

Thrown at src/Aspire.Hosting.Maui/Lifecycle/MauiBuildQueueEventSubscriber.cs:175

            if (semaphoreAcquired && releaseInFinally)
            {
                ReleaseSemaphoreSafely(semaphore);
                logger.LogDebug("Released build lock (resource '{ResourceName}').", resource.Name);
            }

            queueAnnotation.ResourceCancellations.TryRemove(resource.Name, out _);
        }
    }

    /// <summary>
    /// Runs <c>dotnet build</c> as a subprocess and pipes its output to the resource logger.
    /// </summary>
    internal virtual async Task RunBuildAsync(IResource resource, ILogger logger, CancellationToken cancellationToken)
    {
        if (!resource.TryGetLastAnnotation<MauiBuildInfoAnnotation>(out var buildInfo))
        {
            logger.LogWarning("No build info annotation found for resource '{ResourceName}'. Startup cannot proceed.", resource.Name);
            throw new InvalidOperationException(
                $"Resource '{resource.Name}' is missing MauiBuildInfoAnnotation. " +
                "Cannot proceed with build — the semaphore would be held indefinitely.");
        }

        // Match DCP's launch configuration so the Run target starts the exact outputs produced
        // by this serialized build.
        var args = new List<string> { "build", buildInfo.ProjectPath };

        if (!string.IsNullOrEmpty(buildInfo.TargetFramework))
        {
            args.Add("-f");
            args.Add(buildInfo.TargetFramework);
        }

        if (!string.IsNullOrEmpty(buildInfo.Configuration))
        {
            args.Add("--configuration");
            args.Add(buildInfo.Configuration);

View on GitHub (pinned to 25830f84bd)