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
- Create the MAUI platform/project resource through the intended AddMaui*/With* APIs so MauiBuildInfoAnnotation is attached.
- Manually add the annotation before start: resource.Annotations.Add(new MauiBuildInfoAnnotation(...)) if constructing resources by hand.
- 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
- Create MAUI resources only via AddMauiProject/AddMaui* extension APIs
- Filter MAUI lifecycle subscribers on TryGetLastAnnotation<MauiBuildInfoAnnotation>
- Don't strip annotations in custom model transformations
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
- Build failed for resource
- Build for resource ' ' timed out after .
- The MAUI OTLP dev tunnel configuration was not initialized…
- -32000
- -32603
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)