microsoft/aspire · error · InvalidOperationException

ASPIRERADIUS093

ASPIRERADIUS093

Error message

The '${RadiusResourceTypes.SecuritySecrets}' resource '${secret.BicepIdentifier}' has no '${nameof(RadiusSecuritySecretConstruct.EnvironmentId)}'. The type requires 'properties.environment', so Radius would reject the deployment. Assign the environment scope, or remove the resource. Diagnostic: ASPIRERADIUS093.

What it means

During Radius publish, the builder validates that every Applications.Datastores/enhancedSecret (SecuritySecrets) construct has an environment scope because the Radius type requires 'properties.environment'; without it the deployment would be rejected by Radius. The throw happens in ValidateSecuritySecretRequiredFields when EnvironmentId renders to nothing and is not even a deploy-time Bicep expression.

Solutions

  1. Set the EnvironmentId on the RadiusSecuritySecretConstruct to the target Radius environment (e.g. the environment resource ID produced by the builder).
  2. If the environment is only known at deploy time, assign a Bicep expression instead of a literal so validation skips it.
  3. Remove the secret resource if it is not actually needed.

Example fix

// before
var secret = new RadiusSecuritySecretConstruct("appSecrets") { /* EnvironmentId not set */ };
// after
var secret = new RadiusSecuritySecretConstruct("appSecrets") { EnvironmentId = environmentId };
Defensive patterns

Strategy: validation

Validate before calling

if (string.IsNullOrWhiteSpace(secret.EnvironmentId) && !IsBicepExpression(secret.EnvironmentId)) throw new InvalidOperationException($"Secret '{secret.BicepIdentifier}' is missing EnvironmentId.");

Type guard

bool HasEnvironmentId(RadiusSecuritySecretConstruct s) => !string.IsNullOrWhiteSpace(s.EnvironmentId) || IsBicepExpression(s.EnvironmentId);

Prevention

When it happens

Trigger: Adding a RadiusSecuritySecretConstruct to the publish model without assigning its EnvironmentId (the construct was created and added with data entries but the environment scope property was never set, and it is not a Bicep expression).

Common situations: Hand-constructing security secret resources in a custom publish callback and forgetting the environment ID; copying a secret construct from a sample that used a different environment variable; refactoring that moved environment assignment behind a condition that never fires.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

    /// producing a compile error. The result is a resource block that is syntactically fine and is
    /// rejected only by Radius schema validation at <c>rad deploy</c> time, with a message that
    /// points at the generated artifact rather than at the callback that produced it.
    /// </para>
    /// <para>
    /// The encoding vocabulary is the one place the new type diverges from the legacy
    /// <c>Applications.Core/secretStores</c> type it replaces: <c>Radius.Security/secrets</c>
    /// accepts <c>string</c> and <c>base64</c>, where the legacy type accepted <c>raw</c> and
    /// <c>base64</c>. Radius rejects every value outside its own enum, but only <c>raw</c> is
    /// rejected here: it is the legacy vocabulary rather than a value a newer control plane might
    /// introduce, so it is the one spelling that can be called wrong without risking a false
    /// positive on a gate the AppHost author cannot opt out of.
    /// </para>
    /// </remarks>
    private static void ValidateSecuritySecretRequiredFields(RadiusSecuritySecretConstruct secret)
    {
        if (RenderBicepValue(secret.EnvironmentId) is null && !IsBicepExpression(secret.EnvironmentId))
        {
            throw new InvalidOperationException(
                $"The '{RadiusResourceTypes.SecuritySecrets}' resource '{secret.BicepIdentifier}' has no " +
                $"'{nameof(RadiusSecuritySecretConstruct.EnvironmentId)}'. The type requires " +
                $"'properties.environment', so Radius would reject the deployment. Assign the environment scope, " +
                $"or remove the resource. Diagnostic: ASPIRERADIUS093.");
        }

        foreach (var (key, entry) in secret.Data)
        {
            // A callback can leave a hole by assigning null, or by adding a key it never populated.
            // Either way the entry carries no value, which is the same defect the Value check below
            // catches — reported here because there is no construct left to inspect. An entry whose
            // whole wrapper is an expression is a different case: it resolves at deploy time and is
            // skipped rather than rejected, the same way an expression-valued property is.
            if (entry is null || (entry.Value is null && !IsBicepExpression(entry)))
            {
                throw new InvalidOperationException(
                    $"The '{RadiusResourceTypes.SecuritySecrets}' resource '{secret.BicepIdentifier}' has a data " +
                    $"entry '{key}' with no value. The type requires a value for every entry, so Radius would " +

View on GitHub (pinned to 25830f84bd)