microsoft/aspire · error · InvalidOperationException

Radius resource ' ' reads its ' ' from key ' ' of the ' '…

Error message

Radius resource '{credential.Consumer.BicepIdentifier}' reads its '{credential.PropertyName}' from key '{credential.SecretKey}' of the '{RadiusResourceTypes.SecuritySecrets}' resource '{credential.Secret.BicepIdentifier}', but a ConfigureRadiusInfrastructure callback removed that key.

What it means

After running ConfigureRadiusInfrastructure callbacks, the builder validates that each secret key consumed as a required credential property still exists in the secret's Data. If a callback removed the key (or the whole entry), the recipe cannot provision the resource, so this InvalidOperationException with ASPIRERADIUS089 is thrown.

Solutions

  1. Keep the referenced key in the secret's Data dictionary in your callback.
  2. Instead of removing the key, point the consuming '{PropertyName}' at your own secret in the callback.
  3. Audit callbacks that clear or rebuild secret Data so credential keys are preserved.

Example fix

// before
secret.Data.Remove("password"); // consumer still references it

// after
// keep the key, or repoint the consumer property to your own secret resource
Defensive patterns

Strategy: validation

Validate before calling

// Before saving secret mutations in a callback, verify required keys remain
var requiredKeys = new HashSet<string> { "password", "username" }; // keys consumers reference
var missing = requiredKeys.Except(secret.Data.Keys).ToList();
if (missing.Count > 0)
    throw new InvalidOperationException($"Cannot remove credential keys: {string.Join(',', missing)} are referenced by consumers.");

Try / catch

try { await builder.ExecuteCallbacksAsync(ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("ASPIRERADIUS089") && ex.Message.Contains("removed that key"))
{
    logger.LogError(ex, "Callback deleted a consumed secret key; keep it or repoint the consumer.");
}

Prevention

When it happens

Trigger: A ConfigureRadiusInfrastructure callback mutates a generated SecuritySecrets resource and deletes the specific Data key (credential.SecretKey) that a Radius resource's required property (credential.PropertyName) references.

Common situations: Callback code rebuilding the secret's Data dictionary and dropping entries; removing a password/username key it assumed was unused; renames of keys that don't update the consumer references.

Related errors


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

Appendix: source

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

                throw new InvalidOperationException(
                    $"Radius resource '{credential.Consumer.BicepIdentifier}' reads its '{credential.PropertyName}' from " +
                    $"the '{RadiusResourceTypes.SecuritySecrets}' resource '{credential.OriginalSecretIdentifier}', but a " +
                    $"ConfigureRadiusInfrastructure callback removed it. The property is required, so the deployment would " +
                    $"be rejected. Keep the secret, or point '{credential.PropertyName}' at a secret of your own. " +
                    $"Diagnostic: ASPIRERADIUS074.");
            }

            // The consumer still reads this secret, so the entry carrying the credential has to
            // survive intact. Unlike a container env secret — whose only reader is the variable that
            // points at it, so a callback replacing the value is self-consistent — this value is
            // handed to the *recipe* that provisions the server, while the matching credential was
            // already composed into every consumer's connection string from Aspire's own parameter.
            // Removing it prevents the recipe from starting; changing it provisions a server with a
            // password no consumer was told about, which fails only as an authentication error at
            // runtime. Neither can be repaired here, so both are rejected.
            if (!credential.Secret.Data.TryGetValue(credential.SecretKey, out var liveEntry))
            {
                throw new InvalidOperationException(
                    $"Radius resource '{credential.Consumer.BicepIdentifier}' reads its '{credential.PropertyName}' from " +
                    $"key '{credential.SecretKey}' of the '{RadiusResourceTypes.SecuritySecrets}' resource " +
                    $"'{credential.Secret.BicepIdentifier}', but a ConfigureRadiusInfrastructure callback removed that " +
                    $"key. The recipe cannot provision the resource without it. Keep the key, or point " +
                    $"'{credential.PropertyName}' at a secret of your own. Diagnostic: ASPIRERADIUS089.");
            }

            // Both an entry swapped for a new construct and one mutated in place are rejected: the
            // credential Aspire projected to consumers is fixed at this point either way. The
            // encoding is checked alongside the value because it decides how the recipe interprets
            // that value — flipping `string` to `base64` makes the recipe decode before writing the
            // Kubernetes Secret, so the provisioned credential diverges from the one consumers hold
            // even though the value is byte-identical.
            if (!ReferenceEquals(liveEntry?.Value, credential.Entry) ||
                !string.Equals(RenderBicepValue(credential.Entry.Value), credential.OriginalEntryValue, StringComparison.Ordinal) ||
                !string.Equals(RenderBicepValue(credential.Entry.Encoding), credential.OriginalEntryEncoding, StringComparison.Ordinal))
            {
                throw new InvalidOperationException(

View on GitHub (pinned to 25830f84bd)