microsoft/aspire · error · DistributedApplicationException
Unexpected wait behavior
Error message
Unexpected wait behavior: {waitBehavior} What it means
Internal invariant guard in ShouldYieldHealthyWait: the waitBehavior value did not match WaitOnResourceUnavailable or StopOnResourceUnavailable, so the switch fell to the default arm. WaitBehavior is an enum and this indicates an unknown/invalid enum value was supplied.
Solutions
- Only pass explicitly defined members: WaitBehavior.WaitOnResourceUnavailable or WaitBehavior.StopOnResourceUnavailable.
- Audit custom code or extensions that construct WaitAnnotation and remove int casts or default(WaitBehavior) usage.
- Align package versions across all Aspire projects so the enum definition matches the hosting library.
Example fix
// before
var annotation = new WaitAnnotation(dep, WaitType.WaitUntilHealthy) { WaitBehavior = (WaitBehavior)3 };
// after
var annotation = new WaitAnnotation(dep, WaitType.WaitUntilHealthy) { WaitBehavior = WaitBehavior.StopOnResourceUnavailable }; Defensive patterns
Strategy: validation
Validate before calling
bool IsValid(WaitBehavior b) => b is WaitBehavior.WaitOnResourceUnavailable or WaitBehavior.StopOnResourceUnavailable;
if (!IsValid(annotation.WaitBehavior)) throw new ArgumentException($"Unsupported WaitBehavior: {annotation.WaitBehavior}"); Type guard
static bool IsDefinedWaitBehavior(WaitBehavior value) =>
Enum.IsDefined(typeof(WaitBehavior), value); Try / catch
try
{
// wait annotation processing
}
catch (DistributedApplicationException ex) when (ex.Message.Contains("Unexpected wait behavior"))
{
logger.LogError(ex, "Invalid WaitBehavior enum value supplied.");
} Prevention
- Never cast ints to WaitBehavior; use named enum members only.
- Avoid default(WaitBehavior); always set the property explicitly.
- Keep Aspire.Hosting package versions aligned across projects.
- Add a unit test asserting only defined enum values are used in annotations.
When it happens
Trigger: Passing an out-of-range or undefined WaitBehavior value (e.g. a cast of an arbitrary int or a default value that is not a defined member) into a wait annotation / WaitFor API that evaluates ShouldYieldHealthyWait.
Common situations: Custom resource extensions or copy-pasted wait annotation code passing an uninitialized enum (default 0 if not a defined member); library version drift where an older caller passes a WaitBehavior value removed or renamed in a newer Aspire version.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Unexpected wait type
- ConnectionStringAvailableEvent was published for the
- Container tunnel service
- Invalid entrypoint type.
- Service should have valid address at this point
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/d4e4d5a612518e63.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting/ApplicationModel/ResourceNotificationService.cs:295
// Observe the result of the resource ready event task
await resourceEvent.Snapshot.ResourceReadyEvent!.EventTask.WaitAsync(cancellationToken).ConfigureAwait(false);
_logger.LogDebug("Finished waiting for resource '{ResourceName}'.", resourceName);
return resourceEvent;
}
internal static bool ShouldYieldHealthyWait(WaitBehavior waitBehavior, CustomResourceSnapshot snapshot) =>
waitBehavior switch
{
WaitBehavior.WaitOnResourceUnavailable => snapshot.HealthStatus == HealthStatus.Healthy,
WaitBehavior.StopOnResourceUnavailable => snapshot.HealthStatus == HealthStatus.Healthy ||
snapshot.State?.Text == KnownResourceStates.Finished ||
snapshot.State?.Text == KnownResourceStates.Exited ||
snapshot.State?.Text == KnownResourceStates.FailedToStart ||
snapshot.State?.Text == KnownResourceStates.RuntimeUnhealthy,
_ => throw new DistributedApplicationException($"Unexpected wait behavior: {waitBehavior}")
};
private async Task WaitUntilCompletionAsync(IResource resource, IResource dependency, int exitCode, CancellationToken cancellationToken, Func<string, Task>? onDependencyReady = null)
{
using var activity = ProfilingTelemetry.StartResourceWaitForDependency(Configuration, resource, dependency, WaitType.WaitForCompletion, waitBehavior: null);
activity.SetResourceWaitExpectedExitCode(exitCode);
var names = dependency.GetResolvedResourceNames();
var tasks = new Task[names.Length];
var resourceLogger = _resourceLoggerService.GetLogger(resource);
resourceLogger.LogInformation("Waiting for resource '{ResourceName}' to complete.", dependency.Name);
for (var i = 0; i < names.Length; i++)
{
var displayName = names.Length > 1 ? names[i] : dependency.Name;
tasks[i] = Core(displayName, names[i]);
}View on GitHub (pinned to 25830f84bd)