microsoft/aspire · error · RadiusBackingResourceProjectionException

ASPIRERADIUS094

ASPIRERADIUS094

Error message

Resource '${resource.Name}' is emitted as a Radius type whose credential requires a '${RadiusResourceTypes.SecuritySecrets}' resource, but no Radius environment was emitted to scope it to. Diagnostic: ASPIRERADIUS094.

What it means

The resource is emitted as a Radius type whose credential requires a Security.Secrets resource, but no Radius environment construct exists to scope that secret to. Per the code this is an internal invariant: UDT-emitted resources are always parented to the UDT environment, so this asserts the invariant rather than emitting Bicep Radius would reject.

Solutions

  1. Ensure the Radius environment is emitted before resources requiring secrets (check ConfigureRadiusInfrastructure customizations are not suppressing it)
  2. If caused by custom infrastructure callbacks, emit the environment construct before secret-bearing resources
  3. Report the scenario to the Aspire team as a likely internal invariant violation
Defensive patterns

Strategy: try-catch

Validate before calling

// Before publishing, confirm a Radius environment construct exists:
if (envConstruct is null) throw new InvalidOperationException("Radius environment must be emitted before secret-bearing resources");

Type guard

bool HasEnvironment(RadiusEnvironmentConstruct? e) => e is not null;

Try / catch

try { await publisher.BuildAsync(model); }
catch (RadiusBackingResourceProjectionException ex) when (ex.Message.Contains("ASPIRERADIUS094"))
{ /* report internal invariant bug to Aspire */ }

Prevention

When it happens

Trigger: Publishing a resource whose Radius type requires a secret credential (RadiusResourceTypes.SecuritySecrets) when envConstruct is null at line 2676 — i.e. no Radius environment was emitted before the secret was emitted.

Common situations: Effectively a publish-pipeline ordering/invariant bug rather than a user mistake; it may surface after custom ConfigureRadiusInfrastructure callbacks or internal refactors that skip environment emission.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs:2676

    /// that resource's outputs without creating a cycle in the deployment graph.
    /// </para>
    /// </remarks>
    private async Task ApplySecretResourceCredentialsAsync(
        IResource resource,
        IResourceWithConnectionString withConnectionString,
        RadiusResourceTypeConstruct construct,
        RadiusInfrastructureOptions options,
        RadiusEnvironmentConstruct? envConstruct,
        RadiusApplicationConstruct? appConstruct,
        string propertyName,
        string secretKey)
    {
        // The secret's environment scope is required by the type. A resource emitted as a Radius.*
        // UDT is always parented to the UDT environment, so this is unreachable in practice —
        // assert it rather than emitting a secret Radius would reject for a missing required scope.
        if (envConstruct is null)
        {
            throw new RadiusBackingResourceProjectionException(
                resource,
                $"Resource '{resource.Name}' is emitted as a Radius type whose credential requires a " +
                $"'{RadiusResourceTypes.SecuritySecrets}' resource, but no Radius environment was emitted to scope it to. " +
                $"Diagnostic: ASPIRERADIUS094.");
        }

        BicepValue<object>? secretValue = null;

        var credentialParameter = TryGetCredentialParameter(withConnectionString, propertyName);
        if (credentialParameter is not null)
        {
            RegisterRecipeCredential(credentialParameter, resource, isProjectionSubstitution: false);
            secretValue = GetOrAddEnvParameter(credentialParameter);
        }
        else if (await TryResolveConnectionPropertyAsync(withConnectionString, propertyName).ConfigureAwait(false) is { } resolved)
        {
            // Not a bare parameter (a composed expression, or a literal). The resolved value is
            // still built from `@secure()` param references, so no credential lands in the artifact

View on GitHub (pinned to 25830f84bd)