{"record":{"id":"5a91208d77fe2c4d","repo":"microsoft/aspire","slug":"a-configureradiusinfrastructure-callback-changed-the-value","errorCode":null,"errorMessage":"A ConfigureRadiusInfrastructure callback changed the value or encoding of key '{credential.SecretKey}' on the '{RadiusResourceTypes.SecuritySecrets}' resource '{credential.Secret.BicepIdentifier}', which supplies '{credential.PropertyName}' for '{credential.Consumer.BicepIdentifier}'. Consumers were already given the original value.","messagePattern":"A ConfigureRadiusInfrastructure callback changed the value or encoding of key '(.+?)' on the '(.+?)' resource '(.+?)', which supplies '(.+?)' for '(.+?)'\\. Consumers were already given the original value\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs","lineNumber":982,"sourceCode":"                throw new InvalidOperationException(\n                    $\"Radius resource '{credential.Consumer.BicepIdentifier}' reads its '{credential.PropertyName}' from \" +\n                    $\"key '{credential.SecretKey}' of the '{RadiusResourceTypes.SecuritySecrets}' resource \" +\n                    $\"'{credential.Secret.BicepIdentifier}', but a ConfigureRadiusInfrastructure callback removed that \" +\n                    $\"key. The recipe cannot provision the resource without it. Keep the key, or point \" +\n                    $\"'{credential.PropertyName}' at a secret of your own. Diagnostic: ASPIRERADIUS089.\");\n            }\n\n            // Both an entry swapped for a new construct and one mutated in place are rejected: the\n            // credential Aspire projected to consumers is fixed at this point either way. The\n            // encoding is checked alongside the value because it decides how the recipe interprets\n            // that value — flipping `string` to `base64` makes the recipe decode before writing the\n            // Kubernetes Secret, so the provisioned credential diverges from the one consumers hold\n            // even though the value is byte-identical.\n            if (!ReferenceEquals(liveEntry?.Value, credential.Entry) ||\n                !string.Equals(RenderBicepValue(credential.Entry.Value), credential.OriginalEntryValue, StringComparison.Ordinal) ||\n                !string.Equals(RenderBicepValue(credential.Entry.Encoding), credential.OriginalEntryEncoding, StringComparison.Ordinal))\n            {\n                throw new InvalidOperationException(\n                    $\"A ConfigureRadiusInfrastructure callback changed the value or encoding of key '{credential.SecretKey}' \" +\n                    $\"on the '{RadiusResourceTypes.SecuritySecrets}' resource '{credential.Secret.BicepIdentifier}', which supplies \" +\n                    $\"'{credential.PropertyName}' for '{credential.Consumer.BicepIdentifier}'. Consumers were already given \" +\n                    $\"the original credential in their connection strings, so the deployed resource would require a \" +\n                    $\"credential no consumer has. Supply the credential as a parameter instead, or point \" +\n                    $\"'{credential.PropertyName}' at a secret of your own. Diagnostic: ASPIRERADIUS089.\");\n            }\n\n            if (string.Equals(credential.Secret.BicepIdentifier, credential.OriginalSecretIdentifier, StringComparison.Ordinal))\n            {\n                continue;\n            }\n\n            credential.Consumer.SetSchemaProperty(\n                credential.PropertyName,\n                new BicepValue<object>(BuildIdExpression(credential.Secret)));\n        }\n    }","sourceCodeStart":964,"sourceCodeEnd":1000,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs#L964-L1000","documentation":"Consumers' connection strings were already composed from the original secret value before ConfigureRadiusInfrastructure callbacks run. If a callback changes the value or encoding of the consumed secret key, the provisioned resource would use a credential no consumer knows, so the builder throws ASPIRERADIUS089. The check compares both the reference identity and the rendered value/encoding against the originals.","triggerScenarios":"A ConfigureRadiusInfrastructure callback assigns a new value (including byte-identical content with a different encoding, which still produces a different Kubernetes Secret) to the secret entry consumed by credential.Consumer's '{PropertyName}'.","commonSituations":"Callbacks replacing a generated password with an environment-specific one after consumers were already built; changing base64/plain encoding of the entry; reusing the same entry object across callbacks and mutating it in place.","solutions":["Do not mutate the consumed secret key's value or encoding in the callback.","Supply your own credential via a parameter instead of editing the generated secret.","Point the consuming '{PropertyName}' at a separate secret you create in the callback.","If you need a different credential, change it at the Aspire parameter level before the model is composed, not in the callback."],"exampleFix":"// before\ncallback: b =>\n{\n    b.GetSecret(\"admin-password-secret\").Data[\"password\"] = newSecretValue; // diverges from consumers\n}\n\n// after\n// pass the desired credential as a parameter at model time:\nbuilder.AddParameter(\"db-password\", secret: true);\n// then reference that parameter for the resource's password","handlingStrategy":"validation","validationCode":"// In a callback, snapshot the consumed entry before mutating and refuse changes\nvar before = RenderBicepValue(entry.Value) + \":\" + RenderBicepValue(entry.Encoding);\n// ... mutation attempt ...\nif (RenderBicepValue(entry.Value) + \":\" + RenderBicepValue(entry.Encoding) != before)\n    throw new InvalidOperationException(\"Cannot change consumed credential secret value/encoding; supply it as a parameter instead.\");","typeGuard":null,"tryCatchPattern":"try { await builder.ExecuteCallbacksAsync(ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"changed the value or encoding\"))\n{\n    logger.LogError(ex, \"Callback mutated a consumed credential; pass the credential as a parameter instead.\");\n}","preventionTips":["Pass environment-specific credentials as Aspire parameters before model composition, not via secret mutation in callbacks","Do not change secret entry encoding (base64/plain) in callbacks — even identical bytes produce a divergent Kubernetes Secret","Point the consuming property at a separate secret you create rather than editing the generated one"],"tags":["radius","secrets","callback","diagnostic-aspireradius089","credentials"],"backgroundTag":"conflicting-config-options","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}