Extension cancellations are classified as ext.run.failed instead of user.canceled
- Dominant language
- Go
- Stars
- 569
- Forks
- 364
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 136
Description
### Summary
When an interactive azd **extension** command is cancelled by the user, the cancellation is recorded in telemetry as `ext.run.failed` instead of a `user.canceled*` result code. This means extension cancellations are indistinguishable from real failures in telemetry, and any downstream metric that counts cancellations separately for extensions will always read 0.
### Root cause
In `cli/azd/internal/cmd/errors.go`, `classify()` checks error types in order. The `*extensions.ExtensionRunError` case returns early:
```go
// cli/azd/internal/cmd/errors.go
if _, ok := errors.AsType[*extensions.ExtensionRunError](err); ok {
return "ext.run.failed", nil
}
...
if code := classifySentinel(err); code != "" { // user.canceled* is classified here
return code, nil
}
```
The extension runner (`cli/azd/pkg/extensions/runner.go`) wraps **any** error from the extension process in `ExtensionRunError`:
```go
return &runResult, &ExtensionRunError{Err: err, ExtensionId: extension.Id}
```
Because the `ExtensionRunError` check (errors.go:108) runs **before** the cancellation classification (`classifySentinel`, errors.go:140), a cancelled extension run is classified as `ext.run.failed` and the underlying `user.canceled*` sentinel is never reached.
### Impact
- Extension cancellations are counted as failures (`ext.run.failed`) in `RawEventsAppRequests`.
- Success/failure rates for extension commands (`ext.run`) are understated for reliability because user cancellations are lumped in with genuine failures.
- Downstream cooked tables (e.g. an `AzdExtensionCommands` table in `azure-dev-tools`) cannot populate a `CanceledInv`/cancellation metric for extensions.
### Proposed fix
Make cancellation classification take precedence over `ExtensionRunError` so a cancelled extension run emits a distinct `user.canceled*` (or a dedicated `ext.run.canceled`) result code. Options:
1. In `classify()`, unwrap `ExtensionRunError` and check for cancellation sentinels **before** returning `ext.run.failed`, or
2. In the extension runner, detect cancellation and return the cancellation error directly rather than wrapping it in `ExtensionRunError`.
### Notes
- Historical `ext.run.failed` rows cannot be reclassified retroactively; the fix only affects data emitted after it ships.
- Discovered while adding an `AzdExtensionCommands` telemetry table in the internal `azure-dev-tools` repo (coreai-microsoft/azure-dev-tools#96).
Contributor guide
Research direction
Start in cli/azd/internal/cmd/errors.go at classify(), then inspect cli/azd/pkg/extensions/runner.go and the ExtensionRunError wrapper. Verify how cancellation sentinels are propagated and choose a classification path that preserves user.canceled* for canceled extension runs while retaining ext.run.failed for real failures.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli, observability
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100