Skip to content

Fix Dapr sidecar env callback crashing aspire run with Azure resources by deferring endpoint port resolution#1459

Open
gabynevada wants to merge 1 commit into
CommunityToolkit:mainfrom
gabynevada:fix/dapr-defer-sidecar-endpoint-port
Open

Fix Dapr sidecar env callback crashing aspire run with Azure resources by deferring endpoint port resolution#1459
gabynevada wants to merge 1 commit into
CommunityToolkit:mainfrom
gabynevada:fix/dapr-defer-sidecar-endpoint-port

Conversation

@gabynevada

Copy link
Copy Markdown
Contributor

Closes #1458

Overview

In run mode, when an AppHost uses Azure resources, AddAzureContainerAppEnvironment registers an azure-prepare-resources before-start step. That step walks each compute resource and eagerly fires its EnvironmentCallbackAnnotation callbacks to discover Azure role-assignment references — before DCP allocates endpoints.

The Dapr lifecycle hook attaches an environment callback to the app resource that eagerly read the auto-generated <name>-dapr-cli sidecar's http/grpc endpoint .Port:

context.EnvironmentVariables.TryAdd("DAPR_HTTP_PORT", http.Port.ToString(CultureInfo.InvariantCulture));
context.EnvironmentVariables.TryAdd("DAPR_GRPC_PORT", grpc.Port.ToString(CultureInfo.InvariantCulture));

EndpointReference.Port blocks on the resolved allocated value and throws InvalidOperationException: The endpoint 'http' is not allocated for the resource '<name>-dapr-cli' when invoked before allocation — crashing the whole AppHost (exit 134) before anything boots.

Root cause & fix

The two lines above are the only eager endpoint reads in this run-mode path (the sibling DAPR_GRPC_ENDPOINT / DAPR_HTTP_ENDPOINT entries already add the EndpointReference object directly as a deferred IValueProvider and never threw; the daprCli command-line-args callback already uses deferred .Property(EndpointProperty.TargetPort) and is guarded by IsAllocated). The fix replaces the eager reads with 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 (IValueProvider) that the discovery walk collects as a raw reference without resolving, so it no longer throws at before-start — and it still resolves to the same allocated proxy port (DAPR_HTTP_PORT="3500" / DAPR_GRPC_PORT="50001") later. EndpointProperty.Port (the proxy/host port the app connects to) is the correct value here, unchanged from the previous behavior — the CLI args separately use TargetPort for the container-internal port. The now-unused using System.Globalization; was removed to keep the build warning-clean.

This is a non-breaking bug fix. The change is confirmed by the upstream maintainer (microsoft/aspire#18242, closed as by-design — the fix belongs in this repo) and follows the same "defer resolution" precedent already applied to this file in #959.

Tests

A deterministic regression test was added (no Docker, no Azure provisioning): DaprSidecarEndpointAllocationTests.AppEnvironmentCallback_DoesNotThrow_WhenDaprSidecarEndpointsNotAllocated drives the exact failing operation — GetResourceDependenciesAsync(..., ResourceDependencyDiscoveryMode.DirectOnly), the same call AzureResourcePreparer.PrepareResourcesAsync makes per compute resource — with the sidecar endpoints unallocated. It fails with the exact "is not allocated" exception on the pre-fix code and passes on the fix, and additionally asserts the port env vars are present as deferred IValueProviders (guarding against a silent no-op). A secondary end-to-end guard in the Azure test project (DaprAzureContainerAppEnvironmentTests) pins the reported topology using AddAzureContainerAppEnvironment + WithDaprSidecar.

Full CommunityToolkit.Aspire.Hosting.Dapr.Tests suite (net10.0, CI filter): 37 total, 32 passed, 0 failed, 5 pre-existing/unrelated skips.

PR Checklist

  • Created a feature/dev branch in your fork (vs. submitting directly from a commit on main) — branch fix/dapr-defer-sidecar-endpoint-port
  • Based off latest main branch of toolkit
  • PR doesn't include merge commits (always rebase on top of our main, if needed)
  • New integration
    • Docs are written
    • Added description of major feature to project description for NuGet package
  • Tests for the changes have been added (for bug fixes / features) (if applicable)
  • Contains NO breaking changes
  • Every new API (including internal ones) has full XML docs — N/A: no new/changed public or internal API surface; the change is a one-line behavioral fix inside an existing internal lifecycle hook
  • Code follows all style conventions

Other information

  • Adversarial review outcome: the fix was put through a four-lens adversarial review (correctness, regression, test-quality, completeness). No lens refuted the fix; three returned none severity and one returned low. Correctness/regression confirmed (via decompiled Aspire 13.4.3) that the deferred path resolves to the byte-identical env value the eager read produced (same allocated proxy port, same InvariantCulture formatting → 3500/50001), publish/manifest mode is unaffected (callback early-returns on IsPublishMode), and no consumer depends on a plain-string value at gather time. Completeness confirmed the two fixed lines were the only unguarded eager endpoint reads in the run-mode path. The single low finding was optional test hardening — an active test asserting the post-allocation resolved values — which is intentionally not added: resolving a deferred endpoint port requires a fully-started DCP host (a bare manually-set AllocatedEndpoint makes the resolving API block indefinitely), which is exactly why the existing golden DaprTests that asserts 3500/50001 is [Skip]-ed. That contract remains documented by the preserved (unmodified) skipped golden test; the new regression test deliberately fires the callback without resolving, matching the real crash path.
  • Related, out-of-scope: OpenTelemetryCollector endpoint not allocated during run mode when using Azure resources #957 is a twin of this defect in the OpenTelemetry Collector integration (OpenTelemetryCollectorRoutingExtensions.cs eagerly reads endpoint.Url in a LogDebug call, throwing the same "not allocated" error under the same Azure prepare-resources walk). It is intentionally not bundled here to keep this PR narrowly scoped and reviewable; it warrants the same one-line deferred-reference fix in a separate PR.

In run mode, `AddAzureContainerAppEnvironment` registers an
`azure-prepare-resources` before-start step that walks each compute
resource and fires its environment callbacks to discover Azure
role-assignment references — before DCP allocates endpoints. The Dapr
lifecycle hook's callback eagerly read the auto-generated `<name>-dapr-cli`
sidecar's http/grpc endpoint `.Port`, throwing "The endpoint `http` is not
allocated for the resource `<name>-dapr-cli`" and crashing the AppHost
before anything boots (microsoft/aspire#18242).

Defer the port via `EndpointReference.Property(EndpointProperty.Port)` so
the discovery walk collects it as a raw reference without resolving; it
still resolves to the correct allocated proxy port after allocation.

Add regression tests in Dapr.Tests (no Azure) and Azure.Dapr.Tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.sh | bash -s -- 1459

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.ps1) } 1459"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant