{"record":{"id":"68e51eba58ae6d7f","repo":"microsoft/aspire","slug":"destroy-operation-canceled-by-user-helmdeploymentengine","errorCode":null,"errorMessage":"Destroy operation canceled by user.","messagePattern":"Destroy operation canceled by user\\.","errorType":"exception","errorClass":"OperationCanceledException","httpStatus":null,"severity":"info","filePath":"src/Aspire.Hosting.Kubernetes/Deployment/HelmDeploymentEngine.cs","lineNumber":701,"sourceCode":"            }\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            {\n                context.Logger.LogInformation(\"User canceled the destroy operation.\");\n                throw new OperationCanceledException(\"Destroy operation canceled by user.\");\n            }\n        }\n    }\n\n    private static async Task<List<string>> GetServiceEndpointsAsync(\n        string serviceName,\n        string @namespace,\n        string? kubeConfigPath,\n        ILogger logger,\n        CancellationToken cancellationToken)\n    {\n        var endpoints = new List<string>();\n\n        var arguments = $\"get service {serviceName} --namespace {@namespace} -o json\";\n\n        if (kubeConfigPath is not null)\n        {\n            arguments += $\" --kubeconfig \\\"{kubeConfigPath}\\\"\";","sourceCodeStart":683,"sourceCodeEnd":719,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Kubernetes/Deployment/HelmDeploymentEngine.cs#L683-L719","documentation":"Aspire's Helm deployment engine throws OperationCanceledException when the interactive destroy confirmation is declined or fails. Before running 'helm destroy', the engine asks the user to confirm; if the user cancels (or declines), the destroy step is deliberately aborted by throwing so the deploy pipeline stops cleanly. This is intentional cancellation, not a bug.","triggerScenarios":"Calling the Kubernetes deploy/publish pipeline's helmDestroyStep (HelmDeploymentEngine.ConfirmDestroyAsync) and answering 'no'/canceling at the destroy confirmation prompt, or the confirmation result reports Canceled=true / Data=false.","commonSituations":"Developers running 'aspire publish'/'deploy' destroy flows in non-interactive CI where the prompt is auto-canceled; users intentionally aborting a destroy after seeing which resources will be removed; automation that can't respond to the interactive confirmation.","solutions":["If cancellation is intentional, no fix is needed - catch OperationCanceledException around the deploy call and treat it as a graceful abort.","Run the pipeline with confirmation pre-accepted (or use the appropriate non-interactive flag) when running in CI so the prompt does not auto-cancel.","Inspect logs for 'User canceled the destroy operation.' to confirm the cancel came from the confirmation prompt and not an unrelated cancellation token."],"exampleFix":"// before\nawait app.RunAsync(); // crashes with OperationCanceledException when destroy is declined\n// after\ntry\n{\n    await app.RunAsync();\n}\ncatch (OperationCanceledException) when (destroyDeclined)\n{\n    // user declined the destroy confirmation; treat as normal exit\n}","handlingStrategy":"try-catch","validationCode":"// No pre-call validation possible: cancellation depends on the interactive prompt.\n// Decide up front whether your pipeline should destroy, and surface the prompt only where a user can answer.","typeGuard":null,"tryCatchPattern":"try\n{\n    await deployTask;\n}\ncatch (OperationCanceledException ex) when (ex.Message.Contains(\"Destroy operation canceled\"))\n{\n    logger.LogInformation(\"Kubernetes destroy declined by user; aborting pipeline gracefully.\");\n}","preventionTips":["Only invoke destroy flows in interactive sessions where the confirmation prompt can be answered.","For CI, use a non-interactive mode or pre-confirm flag instead of letting the prompt auto-cancel.","Treat OperationCanceledException from deploy as an expected control-flow signal, not a crash."],"tags":["helm","kubernetes","cancellation","deployment"],"backgroundTag":"operation-canceled-by-user","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"}