Skip to content

Dapr sidecar env callback eagerly reads unallocated <name>-dapr-cli endpoint Port, crashing aspire run with Azure resources #1458

Description

@gabynevada

Describe the bug

In run mode, an AppHost that combines Azure resources with a Dapr sidecar crashes at startup before anything boots.

When you call AddAzureContainerAppEnvironment, the Azure hosting integration registers an azure-prepare-resources before-start step. To discover Azure role-assignment references, that step walks the compute resources and eagerly fires their EnvironmentCallbackAnnotation callbacks — before DCP has allocated any endpoints.

The CommunityToolkit Dapr lifecycle hook adds an EnvironmentCallbackAnnotation to the app resource that eagerly reads the auto-generated <name>-dapr-cli sidecar's http/grpc endpoint .Port. Because the endpoints are not yet allocated when the Azure discovery walk runs, the read throws and the whole AppHost dies (exit code 134):

System.InvalidOperationException: The endpoint `http` is not allocated for the resource `<name>-dapr-cli`.

Root cause is in this repo:

  • src/CommunityToolkit.Aspire.Hosting.Dapr/DaprDistributedApplicationLifecycleHook.cs, lines 243-244 (inside the EnvironmentCallbackAnnotation added to the app resource at lines 231-249):
context.EnvironmentVariables.TryAdd("DAPR_HTTP_PORT", http.Port.ToString(CultureInfo.InvariantCulture));
context.EnvironmentVariables.TryAdd("DAPR_GRPC_PORT", grpc.Port.ToString(CultureInfo.InvariantCulture));

http.Port / grpc.Port force resolution of the allocated port and throw when it hasn't been allocated yet. The two lines just below (DAPR_GRPC_ENDPOINT / DAPR_HTTP_ENDPOINT, lines 246-247) add the EndpointReference object directly, so they are deferred and do NOT throw — this is exactly the split that needs fixing.

The upstream tracking issue microsoft/aspire#18242 was closed as by-design: Aspire intends the discovery walk to collect references without resolving them, and env callbacks are expected to supply values as deferred references. So the fix belongs here, in CommunityToolkit.Aspire.Hosting.Dapr.

Regression

Started failing between toolkit 13.3.5 → 13.4.4. The eager .Port read is long-standing, but an upstream multi-network endpoint-allocation timing change flipped it from a latent issue into a hard crash in run mode when Azure resources are present. The fix lives in CommunityToolkit.Aspire.Hosting.Dapr.

Steps to reproduce

1. .NET 10 AppHost. Reference CommunityToolkit.Aspire.Hosting.Dapr (13.x) and an
   Aspire.Hosting.Azure.* integration.
2. Use AddAzureContainerAppEnvironment, add at least one real Azure resource, and add
   a worker with a Dapr sidecar via .WithDaprSidecar():

   var builder = DistributedApplication.CreateBuilder(args);

   builder.AddAzureContainerAppEnvironment("cae");

   var storage = builder.AddAzureStorage("storage");

   builder.AddProject<Projects.Worker>("worker")
          .WithReference(storage)
          .WithDaprSidecar();

   using var app = builder.Build();
   await app.StartAsync();   // throws in azure-prepare-resources

3. Run `aspire run`. Startup aborts (exit 134) before DCP allocates endpoints.

Observed stack trace (abridged):

System.InvalidOperationException: The endpoint `http` is not allocated for the resource `worker-dapr-cli`.
   at Aspire.Hosting.ApplicationModel.EndpointReference.get_Port()
   at CommunityToolkit.Aspire.Hosting.Dapr.DaprDistributedApplicationLifecycleHook
        .<>c__DisplayClass.<OnBeforeStartAsync>b__() in
        DaprDistributedApplicationLifecycleHook.cs:line 243
   at Aspire.Hosting.ApplicationModel.ResourceExtensions
        .GatherRawEnvironmentAndArgumentValuesAsync(...)
   at Aspire.Hosting.Azure.AzureResourcePreparer.BuildRoleAssignmentAnnotations(...)
   at Aspire.Hosting.Azure.AzureResourcePreparer.BeforeStartAsync(...)   // azure-prepare-resources

Expected behavior

aspire run should discover Azure references without resolving endpoint values. The Dapr env callback must supply DAPR_HTTP_PORT / DAPR_GRPC_PORT as deferred reference expressions that are collected as raw references during the azure-prepare-resources walk and resolved later, after DCP allocates the endpoints. Startup should complete normally, and once endpoints are allocated the env vars must resolve to the correct proxy/host ports (e.g. DAPR_HTTP_PORT=3500, DAPR_GRPC_PORT=50001).

Screenshots

N/A

IDE and version

VS Code

IDE version

N/A

Nuget packages

CommunityToolkit.Aspire.Hosting.Dapr (13.x)
Aspire.Hosting.Azure.* (e.g. Aspire.Hosting.Azure.Storage)
.NET 10

Additional context

Root cause pinned to src/CommunityToolkit.Aspire.Hosting.Dapr/DaprDistributedApplicationLifecycleHook.cs, lines 243-244.

Proposed fix — swap the eager .Port.ToString() for a deferred reference expression:

context.EnvironmentVariables.TryAdd("DAPR_HTTP_PORT", http.Property(EndpointProperty.Port));
context.EnvironmentVariables.TryAdd("DAPR_GRPC_PORT", grpc.Property(EndpointProperty.Port));

EndpointReference.Property(EndpointProperty.Port) returns an EndpointReferenceExpression (an IValueProvider / IManifestExpressionProvider) that the discovery walk collects as a raw reference without resolving — so it does not throw at before-start, and still resolves to the correct allocated proxy/host Port later. EndpointProperty is already in scope (used for the CLI args at line 281 as EndpointProperty.TargetPort).

Note on Port vs TargetPort: DAPR_HTTP_PORT / DAPR_GRPC_PORT are placed on the app resource and are consumed by the app to reach the sidecar's Dapr APIs, which in run mode go through the DCP-allocated proxy/host port — so EndpointProperty.Port is correct here. The --dapr-http-port / --dapr-grpc-port CLI flags on the <name>-dapr-cli resource use EndpointProperty.TargetPort because those tell the sidecar process which container-internal port to bind. Different consumer, different port.

This is the exact workaround the maintainer (@karolz-ms) confirmed on microsoft/aspire#18242. After the fix, using System.Globalization; (line 14) becomes unused and can be removed.

Related:

Help us help you

Yes, I'd like to be assigned to work on this item

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions