Move error handling from Program.cs to BaseCommand SetAction delegate#17310
Move error handling from Program.cs to BaseCommand SetAction delegate#17310JamesNK wants to merge 3 commits into
Conversation
Move the OperationCanceledException and generic Exception catch blocks from Program.Main into the BaseCommand try/catch around ExecuteAsync. This centralizes error handling so all commands benefit from consistent cancellation and unexpected error behavior. Also suppress log file path display for cancelled commands by adding CliExitCodes.Cancelled to the suppression list.
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17310Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17310" |
There was a problem hiding this comment.
Pull request overview
This PR refactors Aspire CLI error handling by moving cancellation (OperationCanceledException) and unexpected exception handling from Program.Main into the centralized BaseCommand action wrapper around ExecuteAsync, aiming for consistent behavior across commands.
Changes:
- Centralized handling of cancellation and unexpected exceptions in
BaseCommand, producing consistentCommandResult/exit codes. - Suppressed the “see logs at …” message for additional exit codes (now including
Cancelled). - Removed the corresponding catch blocks from
Program.Main.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Aspire.Cli/Commands/BaseCommand.cs | Adds centralized cancellation/unexpected-exception handling and expands suppression of the log-path message for certain exit codes. |
| src/Aspire.Cli/Program.cs | Removes OperationCanceledException and generic Exception handling from Main. |
IEvangelist
left a comment
There was a problem hiding this comment.
✅ PR Testing Complete — Approved
Dogfooded the PR build (13.4.0-pr.17310.gba7be86c, matches head ba7be86c) on Windows. Refactor works as intended and delivers a real user-visible improvement.
Scenarios
| # | Scenario | Result |
|---|---|---|
| 1 | Install + version verification | ✅ PASS |
| 2 | Regression: aspire --help, aspire new aspire-empty |
✅ PASS — exit 0, no See logs at hint |
| 3 | MissingRequiredArgument (exit 21) suppresses See logs at |
✅ PASS |
| 4 | Cancelled (exit 130) suppresses See logs at |
|
| 5 | Unexpected exception path: error displayed + See logs at shown |
✅ PASS — key behavioral improvement |
| Bonus | Normal failure (exit 7, FailedToFindProject) still shows See logs at |
✅ PASS |
Scenario 5 evidence (the key behavioral change)
aspire init --non-interactive in an empty dir triggered an unhandled exception that escapes ExecuteAsync. Output:
❌ An unexpected error occurred: This prompt requires interactive input but the CLI is running in non-interactive mode. This operation does not yet support non-interactive execution.
📄 See logs at C:\Users\…\.aspire\logs\cli_…log
Exit: 1
Pre-PR this would have been caught only by Program.Main's catch (Exception) and surfaced as a generic log entry with nothing useful on the console. Post-PR, the new generic catch in BaseCommand formats the message via InteractionServiceStrings.UnexpectedErrorOccurred, calls interactionService.DisplayError, records telemetry, returns Failure(InvalidCommand), and the post-execution path shows the See logs at hint. 👍
Scenario 3 evidence (suppression preserved)
❌ A template must be specified when running in non-interactive mode. Use 'aspire new <template>'.
Available values: aspire-starter, aspire-ts-cs-starter, aspire-ts-starter, aspire-py-starter, aspire-ts-empty, aspire-empty
Exit: 21
Exit 21, no See logs at hint — correctly suppressed.
Note on scenario 4 (Ctrl+C)
Empirical SIGINT delivery on Windows from non-interactive PowerShell automation is unreliable: child processes spawned with redirected stdin/out share the parent's console, so AttachConsole+GenerateConsoleCtrlEvent can't reach them. Behavior is symmetric with the verified MissingRequiredArgument suppression (same s_suppressErrorLogsMessageExitCodes array, same check) and the new BaseCommandTests plus this PR's manual testing (3345 tests passed) cover the path.
Conclusion
Small, well-scoped refactor with a real UX improvement. No regressions observed.
|
❓ CLI E2E Tests unknown — 91 passed, 0 failed, 2 unknown (commit View all recordings
📹 Recordings uploaded automatically from CI run #26165336380 |
Description
Move the
OperationCanceledExceptionand genericExceptioncatch blocks fromProgram.Maininto theBaseCommandtry/catch aroundExecuteAsync. This centralizes error handling so all commands benefit from consistent cancellation and unexpected error behavior.Changes
OperationCanceledException(when cancelled orExtensionOperationCanceledException) and genericExceptionto the existing try/catch aroundExecuteAsync. Also addedCliExitCodes.Cancelledto the exit codes that suppress the "see logs at" message since log files don't contain useful context for user-initiated cancellations.Main.Testing
Ran the full CLI test suite — 3345 passed, 3 failed (all pre-existing unrelated failures in
RemoteExecutortests), 12 skipped.