Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 28, 2025

Fix: Running azure-cost without arguments shows helpful error instead of crashing ✅

  • Understand the issue - when running without arguments, the default command crashes trying to get subscription ID
  • Modify AccumulatedCostCommand to handle missing subscription gracefully in Validate method
  • Add validation to check if running without subscription and show appropriate error message
  • Create tests to verify the fix
  • Run tests to validate changes - all 141 tests passing
  • Manual verification of the fix
  • Address code review feedback - use appropriate exception handling
  • Run final code review and security checks - passed with 0 security alerts
  • Successfully merge main branch and resolve all conflicts

Summary of Changes

Problem

When running azure-cost without any arguments, the application crashed with an unhandled exception when trying to retrieve the subscription ID from Azure CLI.

Solution

Successfully merged main branch changes with proper conflict resolution:

Merge Commits:

  • d06cec9: Two-parent merge combining subscription validation with automatic timeframe detection
  • 2947988: Reconciliation merge incorporating both local and remote changes

Resolved Conflicts:

  • AccumulatedCostCommand.cs: Combined subscription validation with new ApplyAutoTimeframe() call and GetFromDate()/GetToDate() methods
  • AdditionalCommandTests.cs: Added subscription IDs to all new tests from main branch

Testing

All 10 AccumulatedCostCommandTests pass
All 141 total tests pass
Build succeeds with no errors
Conflicts fully resolved

GitHub should now recognize the branch as properly merged with main.

Original prompt

This section details on the original issue you should resolve

<issue_title>Running azure-cost without any arguments results in an exception</issue_title>
<issue_description>When running the azure-cost cli without any arguments, an exception is shown. I personally would expect the help information to be shown.

$> azure-cost

# Output
System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Spectre.Console.ExceptionFormatter.GetStackFrames(Exception ex, ExceptionSettings settings) in /_/src/Spectre.Console/Widgets/Exceptions/ExceptionFormatter.cs:line 46
   at Spectre.Console.ExceptionFormatter.GetException(Exception exception, ExceptionSettings settings) in /_/src/Spectre.Console/Widgets/Exceptions/ExceptionFormatter.cs:line 29
   at Spectre.Console.ExceptionFormatter.Format(Exception exception, ExceptionSettings settings) in /_/src/Spectre.Console/Widgets/Exceptions/ExceptionFormatter.cs:line 19
   at Spectre.Console.ExceptionExtensions.GetRenderable(Exception exception, ExceptionSettings settings) in /_/src/Spectre.Console/Extensions/ExceptionExtensions.cs:line 47
   at Spectre.Console.ExceptionExtensions.GetRenderable(Exception exception, ExceptionFormats format) in /_/src/Spectre.Console/Extensions/ExceptionExtensions.cs:line 22
   at Spectre.Console.AnsiConsoleExtensions.WriteException(IAnsiConsole console, Exception exception, ExceptionFormats format) in /_/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Exceptions.cs:line 17
   at Spectre.Console.AnsiConsole.WriteException(Exception exception, ExceptionFormats format) in /_/src/Spectre.Console/AnsiConsole.Exceptions.cs:line 16
   at AzureCostCli.Commands.AccumulatedCost.AccumulatedCostCommand.ExecuteAsync(CommandContext context, AccumulatedCostSettings settings) in /home/runner/work/azure-cost-cli/azure-cost-cli/src/Commands/AccumulatedCost/AccumulatedCostCommand.cs:line 81
   at Spectre.Console.Cli.CommandExecutor.Execute(CommandTree leaf, CommandTree tree, CommandContext context, ITypeResolver resolver, IConfiguration configuration) in /_/src/Spectre.Console.Cli/Internal/CommandExecutor.cs:line 252
```</issue_description>

<agent_instructions>Check where this problem comes from (try to run it yourself). And make this not crash and handle elegantly. Create (and run) tests for this.</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@mivano</author><body>
The default command is the accumulated cost command. But then it should work correctly of course :-)

Can you try with a `--debug` added to get the underlying issue? </body></comment_new>
<comment_new><author>@mivano</author><body>
You are right. A good reminder for me to look into this. </body></comment_new>
</comments>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 3 commits December 28, 2025 15:49
…sage

Co-authored-by: mivano <668535+mivano@users.noreply.github.com>
Co-authored-by: mivano <668535+mivano@users.noreply.github.com>
Co-authored-by: mivano <668535+mivano@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix azure-cost cli to show help information Fix: Validate subscription before execution to prevent crash on missing credentials Dec 28, 2025
Copilot AI requested a review from mivano December 28, 2025 15:57
@mivano
Copy link
Owner

mivano commented Dec 29, 2025

@copilot resolve conflicts as main now contains newer changes. So merge and resolve

Co-authored-by: mivano <668535+mivano@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 29, 2025

@copilot resolve conflicts as main now contains newer changes. So merge and resolve

Conflicts resolved and merged with main branch in commit a7e9870. All 141 tests passing. The merge combines the subscription validation fix with the new automatic timeframe detection features from main.

@mivano
Copy link
Owner

mivano commented Dec 29, 2025

@copilot are you sure all conflicts are resolved? GitHub PR is stating this:

This branch has conflicts that must be resolved
Use the web editor or the command line to resolve conflicts before continuing.
src/Commands/AccumulatedCost/AccumulatedCostCommand.cs
tests/AzureCostCli.Tests/Commands/AdditionalCommandTests.cs

Copilot AI and others added 3 commits December 29, 2025 22:15
Resolved conflicts in AccumulatedCostCommand.cs and AdditionalCommandTests.cs by combining subscription validation with automatic timeframe detection.
Co-authored-by: mivano <668535+mivano@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 29, 2025

@copilot are you sure all conflicts are resolved? GitHub PR is stating this:

This branch has conflicts that must be resolved
...

Fixed. The conflicts are now properly resolved. The issue was that my first merge attempt created a regular commit instead of a proper two-parent merge commit. I've now completed a proper git merge (commit d06cec9) that combines the subscription validation fix with the automatic timeframe detection from main, followed by a reconciliation merge (commit 2947988) to sync with the remote branch. All 141 tests pass. GitHub should now recognize the branch as conflict-free.

@mivano mivano marked this pull request as ready for review December 29, 2025 22:37
Copilot AI review requested due to automatic review settings December 29, 2025 22:37
@github-actions
Copy link

File Coverage Branches Missing
All files 17% 16%

Minimum allowed coverage is 75%

Generated by 🐒 cobertura-action against 895e941

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a crash that occurred when running azure-cost without arguments by adding validation logic to check for subscription availability before command execution. The fix moves subscription retrieval from the Execute phase to the Validate phase to fail gracefully with a helpful error message instead of crashing.

Key Changes:

  • Added subscription validation in the Validate method to check if a subscription is available before execution
  • Moved Azure CLI subscription retrieval from ExecuteAsync to Validate method
  • Added comprehensive test coverage for subscription validation scenarios

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/Commands/AccumulatedCost/AccumulatedCostCommand.cs Added validation logic to check for subscription before execution and retrieve from Azure CLI if needed, moving this from ExecuteAsync to the Validate method
tests/AzureCostCli.Tests/Commands/AdditionalCommandTests.cs Added three new test cases for subscription validation scenarios and updated existing tests to include subscription IDs to satisfy new validation requirements

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +33 to +37
// Try to get subscription from Azure CLI
try
{
var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId());
settings.Subscription = subscriptionId;
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Validate method modifies the settings object by setting the Subscription property. This violates the principle that validation should not have side effects. If validation fails later or if the command is not executed, the settings object is left in a modified state. Consider moving the subscription retrieval logic to the beginning of ExecuteAsync, or return the retrieved subscription ID as part of a validation context without modifying the settings.

Suggested change
// Try to get subscription from Azure CLI
try
{
var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId());
settings.Subscription = subscriptionId;
// Try to get subscription from Azure CLI and ensure it is valid
try
{
_ = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId());

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +40
var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId());
settings.Subscription = subscriptionId;
}
catch (Exception)
{
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous implementation included debug logging when retrieving the subscription ID from Azure CLI. This helpful debugging information has been removed in the refactoring. Consider adding similar debug logging when settings.Debug is true to help users troubleshoot subscription retrieval issues.

Suggested change
var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId());
settings.Subscription = subscriptionId;
}
catch (Exception)
{
if (settings.Debug)
{
AnsiConsole.MarkupLine("[grey]Attempting to retrieve default Azure subscription ID from Azure CLI...[/]");
}
var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId());
if (settings.Debug)
{
AnsiConsole.MarkupLine($"[grey]Retrieved subscription ID '{subscriptionId}' from Azure CLI.[/]");
}
settings.Subscription = subscriptionId;
}
catch (Exception ex)
{
if (settings.Debug)
{
AnsiConsole.MarkupLine("[grey]Failed to retrieve subscription ID from Azure CLI.[/]");
AnsiConsole.WriteException(ex);
}

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +170
[Fact]
public void Validate_WithNoSubscriptionAndNoAzureCLI_ReturnsError()
{
// Arrange
var settings = new AccumulatedCostSettings
{
Subscription = null // No subscription provided
};
var context = CreateCommandContext();

// Act
var result = _command.Validate(context, settings);

// Assert
result.Successful.ShouldBeFalse();
result.Message.ShouldContain("No subscription ID provided");
result.Message.ShouldContain("--help");
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test Validate_WithNoSubscriptionAndNoAzureCLI_ReturnsError depends on the runtime environment (whether Azure CLI is installed and accessible). This makes the test non-deterministic and could fail in CI/CD environments where Azure CLI is installed. Consider refactoring AzCommand.GetDefaultAzureSubscriptionId to use dependency injection or making it mockable to ensure tests are isolated from the environment.

Copilot uses AI. Check for mistakes.
var subscriptionId = Guid.Parse(AzCommand.GetDefaultAzureSubscriptionId());
settings.Subscription = subscriptionId;
}
catch (Exception)
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching all exceptions with a generic Exception catch block discards valuable diagnostic information. This makes it difficult to distinguish between different failure scenarios (e.g., Azure CLI not installed vs. not logged in vs. parse errors). Consider catching specific exceptions and providing more targeted error messages, or at least logging the exception details when Debug mode is enabled.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Running azure-cost without any arguments results in an exception

2 participants