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
- Set the EnvironmentId on the RadiusSecuritySecretConstruct to the target Radius environment (e.g. the environment resource ID produced by the builder).
- If the environment is only known at deploy time, assign a Bicep expression instead of a literal so validation skips it.
- 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
- Always set EnvironmentId immediately when constructing a RadiusSecuritySecretConstruct.
- Use object initializer syntax so required scope properties are visible at the call site.
- Run aspire publish locally before CI to catch validation diagnostics early.
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
- A ConfigureRadiusInfrastructure callback replaced port
- A ConfigureRadiusInfrastructure callback replaced the…
- ASPIRERADIUS056
- ASPIRERADIUS087
- A ConfigureRadiusInfrastructure callback changed port
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)