{"record":{"id":"a18f3ee480374823","repo":"microsoft/aspire","slug":"cannot-perform-destructive-operation-without-confirmation-a18f3e","errorCode":null,"errorMessage":"Cannot perform destructive operation without confirmation. Use --yes to skip the confirmation prompt in non-interactive mode.","messagePattern":"Cannot perform destructive operation without confirmation\\. Use --yes to skip the confirmation prompt in non-interactive mode\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Kubernetes/Deployment/HelmDeploymentEngine.cs","lineNumber":681,"sourceCode":"        }\n\n        context.Logger.LogInformation(\n            \"Skipping Helm cleanup for Kubernetes environment '{EnvironmentName}' because the cluster no longer exists.\",\n            environment.Name);\n        return true;\n    }\n\n    private static async Task ConfirmDestroyAsync(PipelineStepContext context, string message)\n    {\n        var options = context.Services.GetRequiredService<IOptions<PipelineOptions>>();\n\n        if (!options.Value.SkipConfirmation)\n        {\n            var interactionService = context.Services.GetRequiredService<IInteractionService>();\n\n            if (!interactionService.IsAvailable)\n            {\n                throw new InvalidOperationException(\n                    \"Cannot perform destructive operation without confirmation. Use --yes to skip the confirmation prompt in non-interactive mode.\");\n            }\n\n            var result = await interactionService.PromptNotificationAsync(\n                \"Destroy environment\",\n                message,\n                new NotificationInteractionOptions\n                {\n                    Intent = MessageIntent.Confirmation,\n                    ShowSecondaryButton = true,\n                    ShowDismiss = false,\n                    PrimaryButtonText = \"Destroy\",\n                    SecondaryButtonText = \"Cancel\"\n                },\n                context.CancellationToken).ConfigureAwait(false);\n\n            if (result.Canceled || !result.Data)\n            {","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Kubernetes/Deployment/HelmDeploymentEngine.cs#L663-L699","documentation":"Destroying the environment uninstalls the Helm release, which is destructive, so HelmDeploymentEngine requires confirmation. In non-interactive mode (no dashboard/console prompt available via IInteractionService) and without SkipConfirmation (--yes), the engine cannot ask the user, so it refuses to proceed rather than deleting resources unattended.","triggerScenarios":"helmDestroyStep calls ConfirmDestroyAsync while options.Value.SkipConfirmation is false and interactionService.IsAvailable is false - i.e. an unattended/non-interactive 'aspire destroy' run (CI, scripts) that was not started with --yes.","commonSituations":"CI/CD destroy pipelines that omit --yes, scripting aspire destroy against a containerized or headless dashboard where no interactive prompt service exists, or forgetting the flag after it worked interactively on a dev machine.","solutions":["Pass --yes (skip confirmation) to the destroy command when running non-interactive: aspire destroy --yes.","If the run should be interactive, execute it in a session where the Aspire interaction service is available (attached dashboard/console prompt) so the confirmation can be answered.","For automation, gate the destroy step in your pipeline so --yes is only passed after your own approval step."],"exampleFix":"// before (CI script)\naspire destroy\n\n// after\naspire destroy --yes","handlingStrategy":"validation","validationCode":"// non-interactive run: always pass the confirmation-skip flag\n// aspire destroy --yes\n// or check programmatically that the command args include --yes before invoking destroy in CI","typeGuard":null,"tryCatchPattern":"try\n{\n    await deploymentEngine.DestroyAsync(context);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Cannot perform destructive operation without confirmation\"))\n{\n    // re-run with --yes or surface a prompt to the user\n    logger.LogError(ex, \"Destroy requires --yes in non-interactive mode\");\n    throw;\n}","preventionTips":["In CI/scripts, always include --yes on aspire destroy after your own approval gate.","Don't rely on interactive prompts in headless environments - detect non-interactive mode up front and add the flag.","Gate destructive pipeline steps behind manual approval so --yes is never passed unattended."],"tags":["cli","confirmation","non-interactive","destroy"],"backgroundTag":"missing-required-flag","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}